mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-30 14:22:05 +00:00
Compare commits
318 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73b368d202 | ||
|
|
f293a0116b | ||
|
|
ff5bec30c0 | ||
|
|
5c66bed477 | ||
|
|
d2068106e4 | ||
|
|
b7b02854d5 | ||
|
|
0edafc2d8e | ||
|
|
279302737d | ||
|
|
634de67d74 | ||
|
|
cd44368d66 | ||
|
|
ae8aa56602 | ||
|
|
34d6692dae | ||
|
|
8d2f0edbab | ||
|
|
d4d7aaba2b | ||
|
|
d690df86d8 | ||
|
|
e8f3cdbb48 | ||
|
|
33aa38e885 | ||
|
|
2faba71df0 | ||
|
|
1d3a32be5d | ||
|
|
5b8e7d1404 | ||
|
|
85a5d01e72 | ||
|
|
c7b17f2214 | ||
|
|
d8c8a783de | ||
|
|
293b0f71c8 | ||
|
|
e6e0e399e0 | ||
|
|
3223de5d03 | ||
|
|
c6d08d6a3f | ||
|
|
375501f282 | ||
|
|
0a62f0d81e | ||
|
|
112e468c7d | ||
|
|
0ccc601f1b | ||
|
|
ae82bcd7bf | ||
|
|
68512b7e12 | ||
|
|
d6cc4fc028 | ||
|
|
e8c7c065e2 | ||
|
|
427394a105 | ||
|
|
75fc2cbafa | ||
|
|
b9a3393b39 | ||
|
|
ed60677608 | ||
|
|
1a407341ec | ||
|
|
567350771c | ||
|
|
47bef77723 | ||
|
|
12f09b2201 | ||
|
|
77d0b4182e | ||
|
|
caf64e0fa4 | ||
|
|
ac90b89099 | ||
|
|
4523bb8e49 | ||
|
|
06f4898892 | ||
|
|
5bbab10485 | ||
|
|
8fe66d058e | ||
|
|
6a893ac424 | ||
|
|
f184ace301 | ||
|
|
121c2b33da | ||
|
|
215f1021d6 | ||
|
|
ce6159ad93 | ||
|
|
746ea93dbe | ||
|
|
612c05fabc | ||
|
|
dfc246d079 | ||
|
|
0a5983e32b | ||
|
|
9fde629233 | ||
|
|
e089c5e466 | ||
|
|
94b0572958 | ||
|
|
133b5e4794 | ||
|
|
6b0ba573de | ||
|
|
6ae7fc2172 | ||
|
|
9cabfb0e2c | ||
|
|
17f05cb3d8 | ||
|
|
f2839f4ff9 | ||
|
|
9aadcb91fb | ||
|
|
5d4dfe1ac7 | ||
|
|
d3d76dd435 | ||
|
|
0c3086d68b | ||
|
|
9b3b9771e6 | ||
|
|
a92403bda4 | ||
|
|
67905affd3 | ||
|
|
c20cadff94 | ||
|
|
0cc45b2b12 | ||
|
|
b6063f6b20 | ||
|
|
678c61e569 | ||
|
|
e88d6d7844 | ||
|
|
bae43cbfe9 | ||
|
|
68c77cefff | ||
|
|
75980ee18b | ||
|
|
ed30198c8d | ||
|
|
185a671f82 | ||
|
|
42905819a7 | ||
|
|
93d6faa392 | ||
|
|
64337775d6 | ||
|
|
d690cf37fb | ||
|
|
32c8ef3b25 | ||
|
|
607c68967d | ||
|
|
7fb9f63d1f | ||
|
|
a04a2072f7 | ||
|
|
72bd71dea2 | ||
|
|
f87328f874 | ||
|
|
4d9b71d48e | ||
|
|
06f8bc771a | ||
|
|
24747207b8 | ||
|
|
e762c91ff3 | ||
|
|
0fe4636fe1 | ||
|
|
8ab011781e | ||
|
|
7f820bd5a6 | ||
|
|
26b8f3f65c | ||
|
|
f172a00fb7 | ||
|
|
077d22167c | ||
|
|
ebd1582d1c | ||
|
|
5e486d4794 | ||
|
|
537d0c6d8f | ||
|
|
4fc75f5c46 | ||
|
|
baf1bc48b0 | ||
|
|
f26a123646 | ||
|
|
7160e3f4b9 | ||
|
|
24b3161a8e | ||
|
|
383fbd1c49 | ||
|
|
c0f5abdfae | ||
|
|
a50e50d797 | ||
|
|
1f9dac9e3a | ||
|
|
f57ac6d624 | ||
|
|
0b96fe5605 | ||
|
|
16561be854 | ||
|
|
570ad1c984 | ||
|
|
736bcdb994 | ||
|
|
e71472f498 | ||
|
|
fb328f26b8 | ||
|
|
c26aba8b2d | ||
|
|
a17de1de8d | ||
|
|
7ea159cb10 | ||
|
|
8ed98bbedc | ||
|
|
43a1b5b93a | ||
|
|
977754f02c | ||
|
|
10aae5fa15 | ||
|
|
dee60e5e8f | ||
|
|
0c05b77849 | ||
|
|
223954e9ca | ||
|
|
fdc8c50d25 | ||
|
|
aa7872d030 | ||
|
|
9ed7744421 | ||
|
|
c19db362d9 | ||
|
|
6a505eea4e | ||
|
|
99be2764d0 | ||
|
|
b656ad9e7f | ||
|
|
75e5756d7e | ||
|
|
13d8bbf877 | ||
|
|
f43d7fb394 | ||
|
|
68527b2c91 | ||
|
|
347c074368 | ||
|
|
2a74f51f8d | ||
|
|
1a75369591 | ||
|
|
8f6e775ca9 | ||
|
|
bad0fa6c23 | ||
|
|
d4caa6f209 | ||
|
|
723bdf9771 | ||
|
|
49fbd85bb4 | ||
|
|
f040fbf0cf | ||
|
|
efb2213ab7 | ||
|
|
742ac6b17d | ||
|
|
ae7761cb96 | ||
|
|
c977982d64 | ||
|
|
28fc3dca36 | ||
|
|
f1cf65a371 | ||
|
|
beb1d29e8f | ||
|
|
1eda7cef9e | ||
|
|
1af92ad949 | ||
|
|
3fc9b35fe4 | ||
|
|
56b48dbd4d | ||
|
|
1cb371dc5a | ||
|
|
499001a269 | ||
|
|
4c9e47379d | ||
|
|
a0dbb5c81f | ||
|
|
dab53c6bbf | ||
|
|
c0a665c00a | ||
|
|
41b5c57240 | ||
|
|
1d65a79c20 | ||
|
|
efb6e72636 | ||
|
|
af83ffd608 | ||
|
|
0201077bc4 | ||
|
|
91d91d3024 | ||
|
|
9784a2b6c5 | ||
|
|
87a3301912 | ||
|
|
295795edc9 | ||
|
|
6120ab66ba | ||
|
|
7bea180c58 | ||
|
|
a29cee488e | ||
|
|
74b4b04693 | ||
|
|
b8b9c80bdc | ||
|
|
a50fc74117 | ||
|
|
c1af438a3a | ||
|
|
80b251cddc | ||
|
|
7d9a48818e | ||
|
|
ca3da0839b | ||
|
|
cf463d8fa1 | ||
|
|
635fdc4c5c | ||
|
|
3e913931c4 | ||
|
|
46420da038 | ||
|
|
d5b7619dd1 | ||
|
|
89b39123da | ||
|
|
5a43137984 | ||
|
|
761b07450f | ||
|
|
a0b67a06f4 | ||
|
|
59d7adb66d | ||
|
|
0c2f0d23cd | ||
|
|
dc201e6c8f | ||
|
|
9bf3399371 | ||
|
|
28565b691a | ||
|
|
7c6faf66c1 | ||
|
|
b160274859 | ||
|
|
3a1ced65d5 | ||
|
|
afeb72803d | ||
|
|
600c28c2a3 | ||
|
|
ec799fff96 | ||
|
|
60da08cdbc | ||
|
|
bdda3743db | ||
|
|
59fcf11ae3 | ||
|
|
77c80b7695 | ||
|
|
13f168f78c | ||
|
|
324eed7e02 | ||
|
|
4a99c7e9b1 | ||
|
|
01e5917642 | ||
|
|
ae00f4279e | ||
|
|
642c69eb96 | ||
|
|
ac69361735 | ||
|
|
e24efc4948 | ||
|
|
f8ad18ed5a | ||
|
|
17345372a2 | ||
|
|
db76494525 | ||
|
|
e87eda2757 | ||
|
|
06e2729aca | ||
|
|
37ffb88d67 | ||
|
|
3d4437151a | ||
|
|
af1961b185 | ||
|
|
c983e33522 | ||
|
|
b18daec708 | ||
|
|
6d20995e70 | ||
|
|
14cacd1bbc | ||
|
|
9d7acdb892 | ||
|
|
05325913f7 | ||
|
|
5733dca68a | ||
|
|
838d4496e5 | ||
|
|
4967363116 | ||
|
|
fc61308be5 | ||
|
|
5c4a292542 | ||
|
|
aa68c2c3d8 | ||
|
|
9c0ebca66f | ||
|
|
888f177597 | ||
|
|
c69fbcfa68 | ||
|
|
f044db142c | ||
|
|
9af7bc3ac8 | ||
|
|
cd87fcde26 | ||
|
|
9e1a6c804f | ||
|
|
4c15f6eb01 | ||
|
|
4ad2722e81 | ||
|
|
b267b904cc | ||
|
|
1ebe9ff8ba | ||
|
|
623a4956a5 | ||
|
|
6572b846c8 | ||
|
|
fe3ba9fb89 | ||
|
|
c4b9db303a | ||
|
|
aed52fdbf8 | ||
|
|
f9573b3eab | ||
|
|
7a8880ee68 | ||
|
|
68aae32d06 | ||
|
|
4b07f37378 | ||
|
|
4d573c6b9d | ||
|
|
10dcb2380c | ||
|
|
fcbe4377aa | ||
|
|
84db4060e6 | ||
|
|
b73a1c532b | ||
|
|
4fe27067e9 | ||
|
|
9c6981ed19 | ||
|
|
560ac2df68 | ||
|
|
260e6e9daa | ||
|
|
38a1368c76 | ||
|
|
9bf3139bd2 | ||
|
|
d389d1d62a | ||
|
|
e60c0e312c | ||
|
|
dc13beca49 | ||
|
|
6399488819 | ||
|
|
69c1b93d28 | ||
|
|
50c240793a | ||
|
|
80f7783b7d | ||
|
|
259c8b3599 | ||
|
|
ba1295440b | ||
|
|
9e6178037c | ||
|
|
47302815c1 | ||
|
|
c81bfe1457 | ||
|
|
f413b5d498 | ||
|
|
feae1b8bdd | ||
|
|
57bb51fb8d | ||
|
|
0f1364502a | ||
|
|
a508ec0af6 | ||
|
|
a9b264004c | ||
|
|
803b61430f | ||
|
|
25367512c0 | ||
|
|
e3720f4a50 | ||
|
|
1168cc58f4 | ||
|
|
c711b387bb | ||
|
|
a3fe0de233 | ||
|
|
922153837d | ||
|
|
960dd6c76a | ||
|
|
fcb7135c3d | ||
|
|
3102bff729 | ||
|
|
992b16cc1d | ||
|
|
2046872d79 | ||
|
|
7312ae20d1 | ||
|
|
e8f2385928 | ||
|
|
204f9ff4c7 | ||
|
|
17e64f3dd0 | ||
|
|
d09e7e7981 | ||
|
|
95e9d06e47 | ||
|
|
f147c762d5 | ||
|
|
200ce6555b | ||
|
|
b5e5537691 | ||
|
|
3b80725673 | ||
|
|
d7c5be834e | ||
|
|
4a5672fc77 | ||
|
|
34fa2f456e | ||
|
|
663285611b | ||
|
|
a62cc78e18 |
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG] - "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Describe the bug
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
## Expected behavior
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
## To Reproduce
|
||||
<!--
|
||||
Outline the steps required to reproduce the bug, including any specific actions, inputs, or conditions:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
-->
|
||||
|
||||
## Environment
|
||||
- Android device: [Device Model]
|
||||
- Android OS version: [Android Version]
|
||||
- App version: [App Version]
|
||||
- Other relevant details: [e.g., specific network conditions, external dependencies]
|
||||
|
||||
## Logs or Screenshots
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
## Additional context
|
||||
<!-- Add any other context about the problem here. -->
|
||||
41
.github/ISSUE_TEMPLATE/crash-report.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/crash-report.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Crash report
|
||||
about: Tell us how to replicate the crash
|
||||
title: "[CRASH] - "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Description
|
||||
<!-- Provide a clear and concise description of the crash you encountered. -->
|
||||
|
||||
## Steps to Reproduce
|
||||
<!--
|
||||
Please provide the steps to reproduce the crash:
|
||||
1. Go to '...'
|
||||
2. Click on '...'
|
||||
3. Scroll down to '...'
|
||||
4. See the crash
|
||||
-->
|
||||
|
||||
## Environment
|
||||
- Android device: [Device Model]
|
||||
- Android OS version: [Android Version]
|
||||
- App version: [App Version]
|
||||
- Other relevant details: [e.g., specific network conditions, external dependencies]
|
||||
|
||||
## Crash Logs/Stack trace
|
||||
<!-- If available, please provide the crash log or stack trace related to the crash. Include it inside a code block (surround with triple backticks ```). Please use the unsigned apk (app-tempo-debug.apk), as the logs would be illegible and therefore useless for this purpose. -->
|
||||
|
||||
## Screenshots
|
||||
<!-- If applicable, add screenshots to help explain the problem. -->
|
||||
|
||||
## Additional Context
|
||||
<!-- Add any other context about the problem here. -->
|
||||
|
||||
## Reproducibility
|
||||
<!-- Mention the frequency of the crash occurrence (e.g., always, sometimes, occasionally). -->
|
||||
|
||||
## Additional Notes
|
||||
<!-- Include any other notes or details that could be helpful for troubleshooting the crash. -->
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature Request] - "
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
<!-- Provide a concise summary of the feature you are requesting. -->
|
||||
|
||||
## Description
|
||||
<!-- Please describe in detail the feature you would like to see implemented. -->
|
||||
|
||||
## Use Case
|
||||
<!-- Explain why this feature is important and how it would improve the user experience. -->
|
||||
|
||||
## Additional context
|
||||
<!-- Include any additional information, screenshots, or examples that could be helpful in understanding or implementing the feature. -->
|
||||
70
.github/workflows/github_release.yml
vendored
70
.github/workflows/github_release.yml
vendored
@@ -6,19 +6,15 @@ on:
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup JDK 8
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 8
|
||||
|
||||
build:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Cache Gradle and wrapper
|
||||
@@ -32,19 +28,28 @@ jobs:
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Setup build tool version variable
|
||||
shell: bash
|
||||
run: |
|
||||
BUILD_TOOL_VERSION=$(ls /usr/local/lib/android/sdk/build-tools/ | tail -n 1)
|
||||
echo "BUILD_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
|
||||
echo Last build tool version is: $BUILD_TOOL_VERSION
|
||||
|
||||
- name: Build APK
|
||||
id: build
|
||||
run: bash ./gradlew assembleRelease
|
||||
run: bash ./gradlew assembleTempoRelease
|
||||
|
||||
- name: Sign APK
|
||||
id: sign_apk
|
||||
uses: r0adkll/sign-android-release@v1
|
||||
with:
|
||||
releaseDirectory: app/build/outputs/apk/release
|
||||
releaseDirectory: app/build/outputs/apk/tempo/release
|
||||
signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||
alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
env:
|
||||
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
|
||||
|
||||
- name: Make artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
@@ -52,34 +57,6 @@ jobs:
|
||||
name: app-release-signed
|
||||
path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||
|
||||
# - name: Build AAB
|
||||
# run: bash ./gradlew bundleRelease
|
||||
|
||||
# - name: Sign AAB
|
||||
# id: sign_aab
|
||||
# uses: r0adkll/sign-android-release@v1
|
||||
# with:
|
||||
# releaseDirectory: app/build/outputs/bundle/release
|
||||
# signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||
# alias: ${{ secrets.KEY_ALIAS_GITHUB }}
|
||||
# keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
# keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
|
||||
|
||||
# - name: Make artifact
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: app-release-signed
|
||||
# path: ${{steps.sign_aab.outputs.signedReleaseFile}}
|
||||
|
||||
# - name: Build Changelog
|
||||
# id: changelog
|
||||
# uses: ardalanamini/auto-changelog@v3
|
||||
# with:
|
||||
# mention-authors: false
|
||||
# mention-new-contributors: false
|
||||
# include-compare: false
|
||||
# semver: false
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@@ -87,7 +64,6 @@ jobs:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release v${{ github.ref }}
|
||||
body: '> Changelog coming soon'
|
||||
# body: ${{ steps.changelog.outputs.changelog }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
@@ -98,15 +74,5 @@ jobs:
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ${{steps.sign_apk.outputs.signedReleaseFile}}
|
||||
asset_name: app-release.apk
|
||||
asset_name: app-tempo-release.apk
|
||||
asset_content_type: application/zip
|
||||
|
||||
# - name: Upload AAB
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ github.token }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ${{steps.sign_aab.outputs.signedReleaseFile}}
|
||||
# asset_name: app-release.aab
|
||||
# asset_content_type: application/zip
|
||||
|
||||
5
.idea/gradle.xml
generated
5
.idea/gradle.xml
generated
@@ -4,16 +4,15 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="11" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -191,7 +191,7 @@
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<h1 align="center"> Tempo </h1>
|
||||
<br>
|
||||
<p align="center">
|
||||
<img alt="Tempo" title="Tempo" src="mockup/svg/horizontal_logo.svg" width="250">
|
||||
</p>
|
||||
@@ -35,12 +33,8 @@ Tempo does not rely on magic algorithms to decide what you should listen to. Ins
|
||||
<img src="mockup/feat/6_screenshot.png" width=200>
|
||||
<img src="mockup/feat/7_screenshot.png" width=200>
|
||||
<img src="mockup/feat/8_screenshot.png" width=200>
|
||||
<img src="mockup/feat/9_screenshot.png" width=200>
|
||||
</p>
|
||||
|
||||
## Disclaimer
|
||||
Tempo is currently under active development and is in alpha state. This means that the app may contain stability issues, bugs, or incomplete features. While we strive to provide a smooth and reliable experience, please be aware that using Tempo in its current state may not be as stable as a fully released version. I appreciate your understanding and patience as I work towards improving the app.
|
||||
|
||||
## Sponsors
|
||||
Tempo is an open-source project developed and maintained solely by me. I would like to express my heartfelt thanks to all the users who have shown their love and support for Tempo. Your contributions and encouragement mean a lot to me, and they help drive the development and improvement of the app.
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
android {
|
||||
compileSdkVersion 34
|
||||
buildToolsVersion '33.0.0'
|
||||
compileSdk = 34
|
||||
buildToolsVersion = "34.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.cappielloantonio.tempo'
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 34
|
||||
versionCode 5
|
||||
versionName '3.3.0'
|
||||
|
||||
versionCode 24
|
||||
versionName '3.7.0'
|
||||
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -25,6 +25,20 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions += "default"
|
||||
|
||||
productFlavors {
|
||||
tempo {
|
||||
dimension = "default"
|
||||
applicationId 'com.cappielloantonio.tempo'
|
||||
}
|
||||
|
||||
notquitemy {
|
||||
dimension = "default"
|
||||
applicationId "com.cappielloantonio.notquitemy.tempo"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
shrinkResources true
|
||||
@@ -51,54 +65,36 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
// AndroidX
|
||||
implementation 'androidx.core:core-ktx:1.10.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||
implementation 'androidx.room:room-runtime:2.5.1'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation 'androidx.room:room-runtime:2.6.1'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
|
||||
// Google GMS
|
||||
implementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
|
||||
// Android Material
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
|
||||
// SearchBar
|
||||
implementation 'com.paulrybitskyi.persistentsearchview:persistentsearchview:1.1.4'
|
||||
implementation 'com.arthurivanets.adapster:adapster:1.0.13'
|
||||
implementation 'com.google.android.material:material:1.10.0'
|
||||
|
||||
// Glide
|
||||
implementation 'com.github.bumptech.glide:glide:4.15.1'
|
||||
implementation 'com.github.bumptech.glide:annotations:4.15.1'
|
||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||
|
||||
// Media3
|
||||
implementation 'androidx.media3:media3-session:1.0.2'
|
||||
implementation 'androidx.media3:media3-common:1.0.2'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.0.2'
|
||||
implementation 'androidx.media3:media3-ui:1.0.2'
|
||||
implementation 'androidx.media3:media3-cast:1.0.2'
|
||||
implementation 'androidx.media3:media3-session:1.2.1'
|
||||
implementation 'androidx.media3:media3-common:1.2.1'
|
||||
implementation 'androidx.media3:media3-exoplayer:1.2.1'
|
||||
implementation 'androidx.media3:media3-ui:1.2.1'
|
||||
tempoImplementation 'androidx.media3:media3-cast:1.2.1'
|
||||
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
|
||||
annotationProcessor 'androidx.room:room-compiler:2.5.1'
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||
annotationProcessor 'androidx.room:room-compiler:2.6.1'
|
||||
|
||||
// Retrofit
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.6'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||
|
||||
// Crash Report
|
||||
// debugImplementation 'com.balsikandar.android:crashreporter:1.1.0'
|
||||
|
||||
// DB debug
|
||||
// debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
||||
}
|
||||
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
@@ -21,4 +21,5 @@
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-keepattributes SourceFile, LineNumberTable
|
||||
-keep public class * extends java.lang.Exception
|
||||
-keep public class * extends java.lang.Exception
|
||||
-keep class retrofit2.** { *; }
|
||||
@@ -0,0 +1,746 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "1f4e50f90f58fb9cb53c89747d142fd9",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "queue",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "trackOrder",
|
||||
"columnName": "track_order",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastPlay",
|
||||
"columnName": "last_play",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playingChanged",
|
||||
"columnName": "playing_changed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "streamId",
|
||||
"columnName": "stream_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"track_order"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "server",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverName",
|
||||
"columnName": "server_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "password",
|
||||
"columnName": "password",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "address",
|
||||
"columnName": "address",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLowSecurity",
|
||||
"columnName": "low_security",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "recent_search",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "search",
|
||||
"columnName": "search",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"search"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "download",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistId",
|
||||
"columnName": "playlist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistName",
|
||||
"columnName": "playlist_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadState",
|
||||
"columnName": "download_state",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "chronology",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "server",
|
||||
"columnName": "server",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1f4e50f90f58fb9cb53c89747d142fd9')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,790 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "ff99e331b4c34a82c560588c4dd5735f",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "queue",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "trackOrder",
|
||||
"columnName": "track_order",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastPlay",
|
||||
"columnName": "last_play",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playingChanged",
|
||||
"columnName": "playing_changed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "streamId",
|
||||
"columnName": "stream_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"track_order"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "server",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverName",
|
||||
"columnName": "server_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "password",
|
||||
"columnName": "password",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "address",
|
||||
"columnName": "address",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLowSecurity",
|
||||
"columnName": "low_security",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "recent_search",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "search",
|
||||
"columnName": "search",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"search"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "download",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistId",
|
||||
"columnName": "playlist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistName",
|
||||
"columnName": "playlist_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadState",
|
||||
"columnName": "download_state",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "chronology",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "server",
|
||||
"columnName": "server",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "favorite",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "songId",
|
||||
"columnName": "songId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "albumId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "toStar",
|
||||
"columnName": "toStar",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"timestamp"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ff99e331b4c34a82c560588c4dd5735f')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,797 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "6ea111217793c58d54eabb1190dd92ec",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "queue",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "trackOrder",
|
||||
"columnName": "track_order",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastPlay",
|
||||
"columnName": "last_play",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playingChanged",
|
||||
"columnName": "playing_changed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "streamId",
|
||||
"columnName": "stream_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"track_order"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "server",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverName",
|
||||
"columnName": "server_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "password",
|
||||
"columnName": "password",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "address",
|
||||
"columnName": "address",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLowSecurity",
|
||||
"columnName": "low_security",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "recent_search",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "search",
|
||||
"columnName": "search",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"search"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "download",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistId",
|
||||
"columnName": "playlist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistName",
|
||||
"columnName": "playlist_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadState",
|
||||
"columnName": "download_state",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadUri",
|
||||
"columnName": "download_uri",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "chronology",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "server",
|
||||
"columnName": "server",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "favorite",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "songId",
|
||||
"columnName": "songId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "albumId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "toStar",
|
||||
"columnName": "toStar",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"timestamp"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6ea111217793c58d54eabb1190dd92ec')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,997 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 4,
|
||||
"identityHash": "528d037bee0f0575f8e0670ae1b04e00",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "queue",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `track_order` INTEGER NOT NULL, `last_play` INTEGER NOT NULL, `playing_changed` INTEGER NOT NULL, `stream_id` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`track_order`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "trackOrder",
|
||||
"columnName": "track_order",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastPlay",
|
||||
"columnName": "last_play",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playingChanged",
|
||||
"columnName": "playing_changed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "streamId",
|
||||
"columnName": "stream_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"track_order"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "server",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_name` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `address` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `low_security` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverName",
|
||||
"columnName": "server_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "password",
|
||||
"columnName": "password",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "address",
|
||||
"columnName": "address",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isLowSecurity",
|
||||
"columnName": "low_security",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "recent_search",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search` TEXT NOT NULL, PRIMARY KEY(`search`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "search",
|
||||
"columnName": "search",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"search"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "download",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `playlist_id` TEXT, `playlist_name` TEXT, `download_state` INTEGER NOT NULL DEFAULT 1, `download_uri` TEXT DEFAULT '', `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistId",
|
||||
"columnName": "playlist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistName",
|
||||
"columnName": "playlist_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadState",
|
||||
"columnName": "download_state",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "downloadUri",
|
||||
"columnName": "download_uri",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "chronology",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `server` TEXT, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "server",
|
||||
"columnName": "server",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "favorite",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`timestamp` INTEGER NOT NULL, `songId` TEXT, `albumId` TEXT, `artistId` TEXT, `toStar` INTEGER NOT NULL, PRIMARY KEY(`timestamp`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "songId",
|
||||
"columnName": "songId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "albumId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "toStar",
|
||||
"columnName": "toStar",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"timestamp"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "session_media_item",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent_id` TEXT, `is_dir` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `artist` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `cover_art_id` TEXT, `size` INTEGER, `content_type` TEXT, `suffix` TEXT, `transcoding_content_type` TEXT, `transcoded_suffix` TEXT, `duration` INTEGER, `bitrate` INTEGER, `path` TEXT, `is_video` INTEGER NOT NULL, `user_rating` INTEGER, `average_rating` REAL, `play_count` INTEGER, `disc_number` INTEGER, `created` INTEGER, `starred` INTEGER, `album_id` TEXT, `artist_id` TEXT, `type` TEXT, `bookmark_position` INTEGER, `original_width` INTEGER, `original_height` INTEGER, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentId",
|
||||
"columnName": "parent_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDir",
|
||||
"columnName": "is_dir",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "track",
|
||||
"columnName": "track",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArtId",
|
||||
"columnName": "cover_art_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "contentType",
|
||||
"columnName": "content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcoding_content_type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcoded_suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bitrate",
|
||||
"columnName": "bitrate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "is_video",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "user_rating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "average_rating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "playCount",
|
||||
"columnName": "play_count",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "disc_number",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "album_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artist_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmark_position",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalWidth",
|
||||
"columnName": "original_width",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "originalHeight",
|
||||
"columnName": "original_height",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '528d037bee0f0575f8e0670ae1b04e00')"
|
||||
]
|
||||
}
|
||||
}
|
||||
1004
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/5.json
Normal file
1004
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/5.json
Normal file
File diff suppressed because it is too large
Load Diff
1016
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/6.json
Normal file
1016
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/6.json
Normal file
File diff suppressed because it is too large
Load Diff
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/7.json
Normal file
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/7.json
Normal file
File diff suppressed because it is too large
Load Diff
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/8.json
Normal file
1021
app/schemas/com.cappielloantonio.tempo.database.AppDatabase/8.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,50 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
|
||||
<application
|
||||
android:name="App"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:localeConfig="@xml/locale_config"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme.SplashScreen"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:allowBackup="false">
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<!-- Declare that this session demo supports Android Auto. -->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/auto_app_desc" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||
android:value="androidx.media3.cast.DefaultCastOptionsProvider"/>
|
||||
android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
|
||||
|
||||
<meta-data
|
||||
android:name="androidx.car.app.TintableAttributionIcon"
|
||||
android:resource="@drawable/ic_graphic_eq" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.MainActivity"
|
||||
android:windowSoftInputMode="adjustPan|adjustResize"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustPan|adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.MediaService"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="mediaPlayback">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaLibraryService" />
|
||||
<action android:name="androidx.media3.session.MediaBrowserService" />
|
||||
<action android:name="android.media.browse.MediaBrowserService"/>
|
||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".service.DownloaderService"
|
||||
android:exported="true">
|
||||
|
||||
<service
|
||||
android:name=".service.DownloaderService"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="dataSync">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||
android:enabled="false"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="autoStoreLocales"
|
||||
android:value="true" />
|
||||
</service>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -10,7 +10,6 @@ import com.cappielloantonio.tempo.helper.ThemeHelper;
|
||||
import com.cappielloantonio.tempo.subsonic.Subsonic;
|
||||
import com.cappielloantonio.tempo.subsonic.SubsonicPreferences;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
|
||||
public class App extends Application {
|
||||
private static App instance;
|
||||
@@ -22,7 +21,6 @@ public class App extends Application {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
String themePref = sharedPreferences.getString(Preferences.THEME, ThemeHelper.DEFAULT_MODE);
|
||||
ThemeHelper.applyTheme(themePref);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.cappielloantonio.tempo.database;
|
||||
|
||||
import androidx.room.AutoMigration;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
@@ -9,19 +10,23 @@ import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.database.converter.DateConverters;
|
||||
import com.cappielloantonio.tempo.database.dao.ChronologyDao;
|
||||
import com.cappielloantonio.tempo.database.dao.DownloadDao;
|
||||
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||
import com.cappielloantonio.tempo.database.dao.QueueDao;
|
||||
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
|
||||
import com.cappielloantonio.tempo.database.dao.ServerDao;
|
||||
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||
import com.cappielloantonio.tempo.model.Chronology;
|
||||
import com.cappielloantonio.tempo.model.Download;
|
||||
import com.cappielloantonio.tempo.model.Favorite;
|
||||
import com.cappielloantonio.tempo.model.Queue;
|
||||
import com.cappielloantonio.tempo.model.RecentSearch;
|
||||
import com.cappielloantonio.tempo.model.Server;
|
||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||
|
||||
@Database(
|
||||
version = 1,
|
||||
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class}
|
||||
// autoMigrations = {@AutoMigration(from = 61, to = 62)}
|
||||
version = 8,
|
||||
entities = {Queue.class, Server.class, RecentSearch.class, Download.class, Chronology.class, Favorite.class, SessionMediaItem.class},
|
||||
autoMigrations = {@AutoMigration(from = 7, to = 8)}
|
||||
)
|
||||
@TypeConverters({DateConverters.class})
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
@@ -47,4 +52,8 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||
public abstract DownloadDao downloadDao();
|
||||
|
||||
public abstract ChronologyDao chronologyDao();
|
||||
|
||||
public abstract FavoriteDao favoriteDao();
|
||||
|
||||
public abstract SessionMediaItemDao sessionMediaItemDao();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ public interface DownloadDao {
|
||||
@Query("SELECT * FROM download WHERE download_state = 1 ORDER BY artist, album, track ASC")
|
||||
LiveData<List<Download>> getAll();
|
||||
|
||||
@Query("SELECT * FROM download WHERE id = :id")
|
||||
Download getOne(String id);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(Download download);
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.cappielloantonio.tempo.database.dao;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
import com.cappielloantonio.tempo.model.Favorite;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface FavoriteDao {
|
||||
@Query("SELECT * FROM favorite")
|
||||
List<Favorite> getAll();
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
void insert(Favorite favorite);
|
||||
|
||||
@Delete
|
||||
void delete(Favorite favorite);
|
||||
|
||||
@Query("DELETE FROM favorite")
|
||||
void deleteAll();
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface RecentSearchDao {
|
||||
@Query("SELECT * FROM recent_search GROUP BY search ORDER BY search DESC LIMIT :limit")
|
||||
List<String> getRecent(int limit);
|
||||
@Query("SELECT * FROM recent_search ORDER BY search DESC")
|
||||
List<String> getRecent();
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(RecentSearch search);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.cappielloantonio.tempo.database.dao;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
import com.cappielloantonio.tempo.model.Queue;
|
||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface SessionMediaItemDao {
|
||||
@Query("SELECT * FROM session_media_item WHERE id = :id")
|
||||
SessionMediaItem get(String id);
|
||||
|
||||
@Query("SELECT * FROM session_media_item WHERE timestamp = :timestamp")
|
||||
List<SessionMediaItem> get(long timestamp);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
void insert(SessionMediaItem sessionMediaItem);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
void insertAll(List<SessionMediaItem> sessionMediaItems);
|
||||
|
||||
@Query("DELETE FROM session_media_item")
|
||||
void deleteAll();
|
||||
}
|
||||
@@ -5,6 +5,9 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
@@ -15,7 +18,9 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.signature.ObjectKey;
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.Util;
|
||||
import com.google.android.material.elevation.SurfaceColors;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -27,16 +32,53 @@ public class CustomGlideRequest {
|
||||
|
||||
public static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL;
|
||||
|
||||
public static RequestOptions createRequestOptions(Context context, String item) {
|
||||
public enum ResourceType {
|
||||
Unknown,
|
||||
Album,
|
||||
Artist,
|
||||
Folder,
|
||||
Directory,
|
||||
Playlist,
|
||||
Podcast,
|
||||
Radio,
|
||||
Song,
|
||||
}
|
||||
|
||||
public static RequestOptions createRequestOptions(Context context, String item, ResourceType type) {
|
||||
return new RequestOptions()
|
||||
.placeholder(new ColorDrawable(SurfaceColors.SURFACE_5.getColor(context)))
|
||||
.fallback(new ColorDrawable(SurfaceColors.SURFACE_5.getColor(context)))
|
||||
.error(new ColorDrawable(SurfaceColors.SURFACE_5.getColor(context)))
|
||||
.fallback(getPlaceholder(context, type))
|
||||
.error(getPlaceholder(context, type))
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.signature(new ObjectKey(item != null ? item : 0))
|
||||
.transform(new CenterCrop(), new RoundedCorners(CustomGlideRequest.CORNER_RADIUS));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Drawable getPlaceholder(Context context, ResourceType type) {
|
||||
switch (type) {
|
||||
case Album:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_album);
|
||||
case Artist:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_artist);
|
||||
case Folder:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_folder);
|
||||
case Directory:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_directory);
|
||||
case Playlist:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_playlist);
|
||||
case Podcast:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_podcast);
|
||||
case Radio:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_radio);
|
||||
case Song:
|
||||
return AppCompatResources.getDrawable(context, R.drawable.ic_placeholder_song);
|
||||
default:
|
||||
case Unknown:
|
||||
return new ColorDrawable(SurfaceColors.SURFACE_5.getColor(context));
|
||||
}
|
||||
}
|
||||
|
||||
public static String createUrl(String item, int size) {
|
||||
Map<String, String> params = App.getSubsonicClientInstance(false).getParams();
|
||||
|
||||
@@ -46,7 +88,7 @@ public class CustomGlideRequest {
|
||||
uri.append("getCoverArt");
|
||||
|
||||
if (params.containsKey("u") && params.get("u") != null)
|
||||
uri.append("?u=").append(params.get("u"));
|
||||
uri.append("?u=").append(Util.encode(params.get("u")));
|
||||
if (params.containsKey("p") && params.get("p") != null)
|
||||
uri.append("&p=").append(params.get("p"));
|
||||
if (params.containsKey("s") && params.get("s") != null)
|
||||
@@ -71,18 +113,18 @@ public class CustomGlideRequest {
|
||||
private final RequestManager requestManager;
|
||||
private Object item;
|
||||
|
||||
private Builder(Context context, String item) {
|
||||
private Builder(Context context, String item, ResourceType type) {
|
||||
this.requestManager = Glide.with(context);
|
||||
|
||||
if (item != null && !Preferences.isDataSavingMode()) {
|
||||
this.item = createUrl(item, Preferences.getImageSize());
|
||||
}
|
||||
|
||||
requestManager.applyDefaultRequestOptions(createRequestOptions(context, item));
|
||||
requestManager.applyDefaultRequestOptions(createRequestOptions(context, item, type));
|
||||
}
|
||||
|
||||
public static Builder from(Context context, String item) {
|
||||
return new Builder(context, item);
|
||||
public static Builder from(Context context, String item, ResourceType type) {
|
||||
return new Builder(context, item, type);
|
||||
}
|
||||
|
||||
public RequestBuilder<Drawable> build() {
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
package com.cappielloantonio.tempo.helper.recyclerview;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class FastScrollbar extends LinearLayout {
|
||||
private static final int BUBBLE_ANIMATION_DURATION = 100;
|
||||
private static final int TRACK_SNAP_RANGE = 5;
|
||||
|
||||
private TextView bubble;
|
||||
private View handle;
|
||||
private RecyclerView recyclerView;
|
||||
private int height;
|
||||
private boolean isInitialized = false;
|
||||
private ObjectAnimator currentAnimator = null;
|
||||
|
||||
private final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
|
||||
updateBubbleAndHandlePosition();
|
||||
}
|
||||
};
|
||||
|
||||
public interface BubbleTextGetter {
|
||||
String getTextToShowInBubble(int pos);
|
||||
}
|
||||
|
||||
public FastScrollbar(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public FastScrollbar(final Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public FastScrollbar(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
protected void init(Context context) {
|
||||
if (isInitialized) return;
|
||||
isInitialized = true;
|
||||
setOrientation(HORIZONTAL);
|
||||
setClipChildren(false);
|
||||
}
|
||||
|
||||
public void setViewsToUse(@LayoutRes int layoutResId, @IdRes int bubbleResId, @IdRes int handleResId) {
|
||||
final LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
inflater.inflate(layoutResId, this, true);
|
||||
bubble = findViewById(bubbleResId);
|
||||
if (bubble != null) bubble.setVisibility(INVISIBLE);
|
||||
handle = findViewById(handleResId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
height = h;
|
||||
updateBubbleAndHandlePosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(@NonNull MotionEvent event) {
|
||||
final int action = event.getAction();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (event.getX() < handle.getX() - ViewCompat.getPaddingStart(handle)) return false;
|
||||
if (currentAnimator != null) currentAnimator.cancel();
|
||||
if (bubble != null && bubble.getVisibility() == INVISIBLE) showBubble();
|
||||
handle.setSelected(true);
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float y = event.getY();
|
||||
setBubbleAndHandlePosition(y);
|
||||
setRecyclerViewPosition(y);
|
||||
return true;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
handle.setSelected(false);
|
||||
hideBubble();
|
||||
return true;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
public void setRecyclerView(final RecyclerView recyclerView) {
|
||||
if (this.recyclerView != recyclerView) {
|
||||
if (this.recyclerView != null)
|
||||
this.recyclerView.removeOnScrollListener(onScrollListener);
|
||||
this.recyclerView = recyclerView;
|
||||
if (this.recyclerView == null) return;
|
||||
recyclerView.addOnScrollListener(onScrollListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (recyclerView != null) {
|
||||
recyclerView.removeOnScrollListener(onScrollListener);
|
||||
recyclerView = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setRecyclerViewPosition(float y) {
|
||||
if (recyclerView != null) {
|
||||
final int itemCount = recyclerView.getAdapter().getItemCount();
|
||||
float proportion;
|
||||
if (handle.getY() == 0) proportion = 0f;
|
||||
else if (handle.getY() + handle.getHeight() >= height - TRACK_SNAP_RANGE)
|
||||
proportion = 1f;
|
||||
else proportion = y / (float) height;
|
||||
final int targetPos = getValueInRange(0, itemCount - 1, (int) (proportion * (float) itemCount));
|
||||
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(targetPos, 0);
|
||||
final String bubbleText = ((BubbleTextGetter) recyclerView.getAdapter()).getTextToShowInBubble(targetPos);
|
||||
if (bubble != null) {
|
||||
bubble.setText(bubbleText);
|
||||
if (TextUtils.isEmpty(bubbleText)) {
|
||||
hideBubble();
|
||||
} else if (bubble.getVisibility() == View.INVISIBLE) {
|
||||
showBubble();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getValueInRange(int min, int max, int value) {
|
||||
int minimum = Math.max(min, value);
|
||||
return Math.min(minimum, max);
|
||||
}
|
||||
|
||||
private void updateBubbleAndHandlePosition() {
|
||||
if (bubble == null || handle.isSelected()) return;
|
||||
|
||||
final int verticalScrollOffset = recyclerView.computeVerticalScrollOffset();
|
||||
final int verticalScrollRange = recyclerView.computeVerticalScrollRange();
|
||||
float proportion = (float) verticalScrollOffset / ((float) verticalScrollRange - height);
|
||||
setBubbleAndHandlePosition(height * proportion);
|
||||
}
|
||||
|
||||
private void setBubbleAndHandlePosition(float y) {
|
||||
final int handleHeight = handle.getHeight();
|
||||
handle.setY(getValueInRange(0, height - handleHeight, (int) (y - handleHeight / 2)));
|
||||
if (bubble != null) {
|
||||
int bubbleHeight = bubble.getHeight();
|
||||
bubble.setY(getValueInRange(0, height - bubbleHeight - handleHeight / 2, (int) (y - bubbleHeight)));
|
||||
}
|
||||
}
|
||||
|
||||
private void showBubble() {
|
||||
if (bubble == null) return;
|
||||
bubble.setVisibility(VISIBLE);
|
||||
if (currentAnimator != null) currentAnimator.cancel();
|
||||
currentAnimator = ObjectAnimator.ofFloat(bubble, "alpha", 0f, 1f).setDuration(BUBBLE_ANIMATION_DURATION);
|
||||
currentAnimator.start();
|
||||
}
|
||||
|
||||
private void hideBubble() {
|
||||
if (bubble == null) return;
|
||||
if (currentAnimator != null) currentAnimator.cancel();
|
||||
currentAnimator = ObjectAnimator.ofFloat(bubble, "alpha", 1f, 0f).setDuration(BUBBLE_ANIMATION_DURATION);
|
||||
currentAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
bubble.setVisibility(INVISIBLE);
|
||||
currentAnimator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
super.onAnimationCancel(animation);
|
||||
bubble.setVisibility(INVISIBLE);
|
||||
currentAnimator = null;
|
||||
}
|
||||
});
|
||||
currentAnimator.start();
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public interface ClickCallback {
|
||||
default void onServerClick(Bundle bundle) {}
|
||||
default void onServerLongClick(Bundle bundle) {}
|
||||
default void onPodcastEpisodeClick(Bundle bundle) {}
|
||||
default void onPodcastEpisodeAltClick(Bundle bundle) {}
|
||||
default void onPodcastEpisodeLongClick(Bundle bundle) {}
|
||||
default void onPodcastChannelClick(Bundle bundle) {}
|
||||
default void onPodcastChannelLongClick(Bundle bundle) {}
|
||||
@@ -28,4 +29,7 @@ public interface ClickCallback {
|
||||
default void onMusicFolderClick(Bundle bundle) {}
|
||||
default void onMusicDirectoryClick(Bundle bundle) {}
|
||||
default void onMusicIndexClick(Bundle bundle) {}
|
||||
default void onDownloadGroupLongClick(Bundle bundle) {}
|
||||
default void onShareClick(Bundle bundle) {}
|
||||
default void onShareLongClick(Bundle bundle) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.cappielloantonio.tempo.interfaces;
|
||||
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
public interface DialogClickCallback {
|
||||
default void onPositiveClick() {}
|
||||
|
||||
default void onNegativeClick() {}
|
||||
|
||||
default void onNeutralClick() {}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.cappielloantonio.tempo.interfaces;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
public interface PlaylistCallback {
|
||||
default void onDismiss() {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.cappielloantonio.tempo.interfaces;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
public interface StarCallback {
|
||||
default void onError() {}
|
||||
default void onSuccess() {}
|
||||
}
|
||||
@@ -20,6 +20,9 @@ class Download(@PrimaryKey override val id: String) : Child(id) {
|
||||
@ColumnInfo(name = "download_state", defaultValue = "1")
|
||||
var downloadState: Int = 0
|
||||
|
||||
@ColumnInfo(name = "download_uri", defaultValue = "")
|
||||
var downloadUri: String? = null
|
||||
|
||||
constructor(child: Child) : this(child.id) {
|
||||
parentId = child.parentId
|
||||
isDir = child.isDir
|
||||
@@ -52,4 +55,10 @@ class Download(@PrimaryKey override val id: String) : Child(id) {
|
||||
originalWidth = child.originalWidth
|
||||
originalHeight = child.originalHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
data class DownloadStack(
|
||||
var id: String,
|
||||
var view: String?
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.cappielloantonio.tempo.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Keep
|
||||
@Parcelize
|
||||
@Entity(tableName = "favorite")
|
||||
data class Favorite(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "timestamp")
|
||||
var timestamp: Long,
|
||||
|
||||
@ColumnInfo(name = "songId")
|
||||
val songId: String?,
|
||||
|
||||
@ColumnInfo(name = "albumId")
|
||||
val albumId: String?,
|
||||
|
||||
@ColumnInfo(name = "artistId")
|
||||
val artistId: String?,
|
||||
|
||||
@ColumnInfo(name = "toStar")
|
||||
val toStar: Boolean,
|
||||
) : Parcelable {
|
||||
override fun toString(): String = (songId ?: "null") + (albumId ?: "null") + (artistId ?: "null")
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.cappielloantonio.tempo.model
|
||||
|
||||
import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
data class ReplayGain(
|
||||
var trackGain: Float = 0f,
|
||||
var albumGain: Float = 0f,
|
||||
)
|
||||
@@ -0,0 +1,281 @@
|
||||
package com.cappielloantonio.tempo.model
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.Keep
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaItem.RequestMetadata
|
||||
import androidx.media3.common.MediaMetadata
|
||||
import androidx.media3.common.MimeTypes
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation
|
||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode
|
||||
import com.cappielloantonio.tempo.util.Constants
|
||||
import com.cappielloantonio.tempo.util.MusicUtil
|
||||
import com.cappielloantonio.tempo.util.Preferences.getImageSize
|
||||
import java.util.Date
|
||||
|
||||
@UnstableApi
|
||||
@Keep
|
||||
@Entity(tableName = "session_media_item")
|
||||
class SessionMediaItem() {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "index")
|
||||
var index: Int = 0
|
||||
|
||||
@ColumnInfo(name = "id")
|
||||
var id: String? = null
|
||||
|
||||
@ColumnInfo(name = "parent_id")
|
||||
var parentId: String? = null
|
||||
|
||||
@ColumnInfo(name = "is_dir")
|
||||
var isDir: Boolean = false
|
||||
|
||||
@ColumnInfo
|
||||
var title: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var album: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var artist: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var track: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var year: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var genre: String? = null
|
||||
|
||||
@ColumnInfo(name = "cover_art_id")
|
||||
var coverArtId: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var size: Long? = null
|
||||
|
||||
@ColumnInfo(name = "content_type")
|
||||
var contentType: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var suffix: String? = null
|
||||
|
||||
@ColumnInfo("transcoding_content_type")
|
||||
var transcodedContentType: String? = null
|
||||
|
||||
@ColumnInfo(name = "transcoded_suffix")
|
||||
var transcodedSuffix: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var duration: Int? = null
|
||||
|
||||
@ColumnInfo("bitrate")
|
||||
var bitrate: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var path: String? = null
|
||||
|
||||
@ColumnInfo(name = "is_video")
|
||||
var isVideo: Boolean = false
|
||||
|
||||
@ColumnInfo(name = "user_rating")
|
||||
var userRating: Int? = null
|
||||
|
||||
@ColumnInfo(name = "average_rating")
|
||||
var averageRating: Double? = null
|
||||
|
||||
@ColumnInfo(name = "play_count")
|
||||
var playCount: Long? = null
|
||||
|
||||
@ColumnInfo(name = "disc_number")
|
||||
var discNumber: Int? = null
|
||||
|
||||
@ColumnInfo
|
||||
var created: Date? = null
|
||||
|
||||
@ColumnInfo
|
||||
var starred: Date? = null
|
||||
|
||||
@ColumnInfo(name = "album_id")
|
||||
var albumId: String? = null
|
||||
|
||||
@ColumnInfo(name = "artist_id")
|
||||
var artistId: String? = null
|
||||
|
||||
@ColumnInfo
|
||||
var type: String? = null
|
||||
|
||||
@ColumnInfo(name = "bookmark_position")
|
||||
var bookmarkPosition: Long? = null
|
||||
|
||||
@ColumnInfo(name = "original_width")
|
||||
var originalWidth: Int? = null
|
||||
|
||||
@ColumnInfo(name = "original_height")
|
||||
var originalHeight: Int? = null
|
||||
|
||||
@ColumnInfo(name = "stream_id")
|
||||
var streamId: String? = null
|
||||
|
||||
@ColumnInfo(name = "stream_url")
|
||||
var streamUrl: String? = null
|
||||
|
||||
@ColumnInfo(name = "timestamp")
|
||||
var timestamp: Long? = null
|
||||
|
||||
constructor(child: Child) : this() {
|
||||
id = child.id
|
||||
parentId = child.parentId
|
||||
isDir = child.isDir
|
||||
title = child.title
|
||||
album = child.album
|
||||
artist = child.artist
|
||||
track = child.track
|
||||
year = child.year
|
||||
genre = child.genre
|
||||
coverArtId = child.coverArtId
|
||||
size = child.size
|
||||
contentType = child.contentType
|
||||
suffix = child.suffix
|
||||
transcodedContentType = child.transcodedContentType
|
||||
transcodedSuffix = child.transcodedSuffix
|
||||
duration = child.duration
|
||||
bitrate = child.bitrate
|
||||
path = child.path
|
||||
isVideo = child.isVideo
|
||||
userRating = child.userRating
|
||||
averageRating = child.averageRating
|
||||
playCount = child.playCount
|
||||
discNumber = child.discNumber
|
||||
created = child.created
|
||||
starred = child.starred
|
||||
albumId = child.albumId
|
||||
artistId = child.artistId
|
||||
type = Constants.MEDIA_TYPE_MUSIC
|
||||
bookmarkPosition = child.bookmarkPosition
|
||||
originalWidth = child.originalWidth
|
||||
originalHeight = child.originalHeight
|
||||
}
|
||||
|
||||
constructor(podcastEpisode: PodcastEpisode) : this() {
|
||||
id = podcastEpisode.id
|
||||
parentId = podcastEpisode.parentId
|
||||
isDir = podcastEpisode.isDir
|
||||
title = podcastEpisode.title
|
||||
album = podcastEpisode.album
|
||||
artist = podcastEpisode.artist
|
||||
year = podcastEpisode.year
|
||||
genre = podcastEpisode.genre
|
||||
coverArtId = podcastEpisode.coverArtId
|
||||
size = podcastEpisode.size
|
||||
contentType = podcastEpisode.contentType
|
||||
suffix = podcastEpisode.suffix
|
||||
duration = podcastEpisode.duration
|
||||
bitrate = podcastEpisode.bitrate
|
||||
path = podcastEpisode.path
|
||||
isVideo = podcastEpisode.isVideo
|
||||
created = podcastEpisode.created
|
||||
artistId = podcastEpisode.artistId
|
||||
streamId = podcastEpisode.streamId
|
||||
type = Constants.MEDIA_TYPE_PODCAST
|
||||
}
|
||||
|
||||
constructor(internetRadioStation: InternetRadioStation) : this() {
|
||||
id = internetRadioStation.id
|
||||
title = internetRadioStation.name
|
||||
streamUrl = internetRadioStation.streamUrl
|
||||
type = Constants.MEDIA_TYPE_RADIO
|
||||
}
|
||||
|
||||
fun getMediaItem(): MediaItem {
|
||||
val uri: Uri = getStreamUri()
|
||||
val artworkUri = Uri.parse(CustomGlideRequest.createUrl(coverArtId, getImageSize()))
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putString("id", id)
|
||||
bundle.putString("parentId", parentId)
|
||||
bundle.putBoolean("isDir", isDir)
|
||||
bundle.putString("title", title)
|
||||
bundle.putString("album", album)
|
||||
bundle.putString("artist", artist)
|
||||
bundle.putInt("track", track ?: 0)
|
||||
bundle.putInt("year", year ?: 0)
|
||||
bundle.putString("genre", genre)
|
||||
bundle.putString("coverArtId", coverArtId)
|
||||
bundle.putLong("size", size ?: 0)
|
||||
bundle.putString("contentType", contentType)
|
||||
bundle.putString("suffix", suffix)
|
||||
bundle.putString("transcodedContentType", transcodedContentType)
|
||||
bundle.putString("transcodedSuffix", transcodedSuffix)
|
||||
bundle.putInt("duration", duration ?: 0)
|
||||
bundle.putInt("bitrate", bitrate ?: 0)
|
||||
bundle.putString("path", path)
|
||||
bundle.putBoolean("isVideo", isVideo)
|
||||
bundle.putInt("userRating", userRating ?: 0)
|
||||
bundle.putDouble("averageRating", averageRating ?: .0)
|
||||
bundle.putLong("playCount", playCount ?: 0)
|
||||
bundle.putInt("discNumber", discNumber ?: 0)
|
||||
bundle.putLong("created", created?.time ?: 0)
|
||||
bundle.putLong("starred", starred?.time ?: 0)
|
||||
bundle.putString("albumId", albumId)
|
||||
bundle.putString("artistId", artistId)
|
||||
bundle.putString("type", Constants.MEDIA_TYPE_MUSIC)
|
||||
bundle.putLong("bookmarkPosition", bookmarkPosition ?: 0)
|
||||
bundle.putInt("originalWidth", originalWidth ?: 0)
|
||||
bundle.putInt("originalHeight", originalHeight ?: 0)
|
||||
bundle.putString("uri", uri.toString())
|
||||
|
||||
return MediaItem.Builder()
|
||||
.setMediaId(id!!)
|
||||
.setMediaMetadata(
|
||||
MediaMetadata.Builder()
|
||||
.setTitle(MusicUtil.getReadableString(title))
|
||||
.setTrackNumber(track ?: 0)
|
||||
.setDiscNumber(discNumber ?: 0)
|
||||
.setReleaseYear(year ?: 0)
|
||||
.setAlbumTitle(MusicUtil.getReadableString(album))
|
||||
.setArtist(MusicUtil.getReadableString(artist))
|
||||
.setArtworkUri(artworkUri)
|
||||
.setExtras(bundle)
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.build()
|
||||
)
|
||||
.setRequestMetadata(
|
||||
RequestMetadata.Builder()
|
||||
.setMediaUri(uri)
|
||||
.setExtras(bundle)
|
||||
.build()
|
||||
)
|
||||
.setMimeType(MimeTypes.BASE_TYPE_AUDIO)
|
||||
.setUri(uri)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun getStreamUri(): Uri {
|
||||
return when (type) {
|
||||
Constants.MEDIA_TYPE_MUSIC -> {
|
||||
MusicUtil.getStreamUri(id)
|
||||
}
|
||||
|
||||
Constants.MEDIA_TYPE_PODCAST -> {
|
||||
MusicUtil.getStreamUri(streamId)
|
||||
}
|
||||
|
||||
Constants.MEDIA_TYPE_RADIO -> {
|
||||
Uri.parse(streamUrl)
|
||||
}
|
||||
|
||||
else -> {
|
||||
MusicUtil.getStreamUri(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,6 @@ import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class AlbumRepository {
|
||||
private static final String TAG = "AlbumRepository";
|
||||
|
||||
public MutableLiveData<List<AlbumID3>> getAlbums(String type, int size, Integer fromYear, Integer toYear) {
|
||||
MutableLiveData<List<AlbumID3>> listLiveAlbums = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
@@ -78,40 +76,6 @@ public class AlbumRepository {
|
||||
return starredAlbums;
|
||||
}
|
||||
|
||||
public void star(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.star(null, id, null)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void unstar(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.unstar(null, id, null)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setRating(String id, int rating) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
@@ -141,7 +105,9 @@ public class AlbumRepository {
|
||||
List<Child> tracks = new ArrayList<>();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) {
|
||||
tracks.addAll(response.body().getSubsonicResponse().getAlbum().getSongs());
|
||||
if (response.body().getSubsonicResponse().getAlbum().getSongs() != null) {
|
||||
tracks.addAll(response.body().getSubsonicResponse().getAlbum().getSongs());
|
||||
}
|
||||
}
|
||||
|
||||
albumTracks.setValue(tracks);
|
||||
@@ -283,7 +249,7 @@ public class AlbumRepository {
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
|
||||
if (response.body().getSubsonicResponse().getAlbumList2().getAlbums().size() > 0 && !response.body().getSubsonicResponse().getAlbumList2().getAlbums().isEmpty()) {
|
||||
callback.onLoadYear(response.body().getSubsonicResponse().getAlbumList2().getAlbums().get(0).getYear());
|
||||
} else {
|
||||
|
||||
@@ -63,8 +63,12 @@ public class ArtistRepository {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
List<ArtistID3> artists = new ArrayList<>();
|
||||
|
||||
for (IndexID3 index : response.body().getSubsonicResponse().getArtists().getIndices()) {
|
||||
artists.addAll(index.getArtists());
|
||||
if(response.body().getSubsonicResponse().getArtists() != null && response.body().getSubsonicResponse().getArtists().getIndices() != null) {
|
||||
for (IndexID3 index : response.body().getSubsonicResponse().getArtists().getIndices()) {
|
||||
if(index != null && index.getArtists() != null) {
|
||||
artists.addAll(index.getArtists());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (random) {
|
||||
@@ -135,9 +139,6 @@ public class ArtistRepository {
|
||||
return artist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Metodo che mi restituisce le informazioni complete dell'artista (bio, immagini prese da last.fm, artisti simili...)
|
||||
*/
|
||||
public MutableLiveData<ArtistInfo2> getArtistFullInfo(String id) {
|
||||
MutableLiveData<ArtistInfo2> artistFullInfo = new MutableLiveData<>(null);
|
||||
|
||||
@@ -161,40 +162,6 @@ public class ArtistRepository {
|
||||
return artistFullInfo;
|
||||
}
|
||||
|
||||
public void star(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.star(null, null, id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void unstar(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.unstar(null, null, id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setRating(String id, int rating) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
|
||||
@@ -0,0 +1,957 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.LibraryResult;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||
import com.cappielloantonio.tempo.database.dao.SessionMediaItemDao;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.model.SessionMediaItem;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Directory;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Index;
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class AutomotiveRepository {
|
||||
private final SessionMediaItemDao sessionMediaItemDao = AppDatabase.getInstance().sessionMediaItemDao();
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbums(String prefix, String type, int size) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getAlbumList2(type, size, 0, null, null)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
|
||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getAlbumList2().getAlbums();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (AlbumID3 album : albums) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setAlbumTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredSongs() {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getStarred2()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getSongs() != null) {
|
||||
List<Child> songs = response.body().getSubsonicResponse().getStarred2().getSongs();
|
||||
|
||||
setChildrenMetadata(songs);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredAlbums(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getStarred2()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getAlbums() != null) {
|
||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getStarred2().getAlbums();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (AlbumID3 album : albums) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getStarredArtists(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getStarred2()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getStarred2() != null && response.body().getSubsonicResponse().getStarred2().getArtists() != null) {
|
||||
List<ArtistID3> artists = response.body().getSubsonicResponse().getStarred2().getArtists();
|
||||
|
||||
Collections.shuffle(artists);
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (ArtistID3 artist : artists) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(artist.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(artist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + artist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getMusicFolders(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getMusicFolders()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getMusicFolders() != null && response.body().getSubsonicResponse().getMusicFolders().getMusicFolders() != null) {
|
||||
List<MusicFolder> musicFolders = response.body().getSubsonicResponse().getMusicFolders().getMusicFolders();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (MusicFolder musicFolder : musicFolders) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(musicFolder.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + musicFolder.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getIndexes(String prefix, String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getIndexes(id, null)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getIndexes() != null) {
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
if (response.body().getSubsonicResponse().getIndexes().getIndices() != null) {
|
||||
List<Index> indices = response.body().getSubsonicResponse().getIndexes().getIndices();
|
||||
|
||||
for (Index index : indices) {
|
||||
if (index.getArtists() != null) {
|
||||
for (Artist artist : index.getArtists()) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(artist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ARTIST)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + artist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body().getSubsonicResponse().getIndexes().getChildren() != null) {
|
||||
List<Child> children = response.body().getSubsonicResponse().getIndexes().getChildren();
|
||||
|
||||
for (Child song : children) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(song.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(song.getTitle())
|
||||
.setAlbumTitle(song.getAlbum())
|
||||
.setArtist(song.getArtist())
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + song.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(MusicUtil.getStreamUri(song.getId()))
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setChildrenMetadata(children);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getDirectories(String prefix, String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getMusicDirectory(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getDirectory() != null && response.body().getSubsonicResponse().getDirectory().getChildren() != null) {
|
||||
Directory directory = response.body().getSubsonicResponse().getDirectory();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (Child child : directory.getChildren()) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(child.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(child.getTitle())
|
||||
.setIsBrowsable(child.isDir())
|
||||
.setIsPlayable(!child.isDir())
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_FOLDER_MIXED)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(child.isDir() ? prefix + child.getId() : child.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(!child.isDir() ? MusicUtil.getStreamUri(child.getId()) : Uri.parse(""))
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setChildrenMetadata(directory.getChildren().stream().filter(child -> !child.isDir()).collect(Collectors.toList()));
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getPlaylists(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPlaylistClient()
|
||||
.getPlaylists()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylists() != null && response.body().getSubsonicResponse().getPlaylists().getPlaylists() != null) {
|
||||
List<Playlist> playlists = response.body().getSubsonicResponse().getPlaylists().getPlaylists();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (Playlist playlist : playlists) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(playlist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + playlist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getNewestPodcastEpisodes(int count) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPodcastClient()
|
||||
.getNewestPodcasts(count)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getNewestPodcasts() != null && response.body().getSubsonicResponse().getNewestPodcasts().getEpisodes() != null) {
|
||||
List<PodcastEpisode> episodes = response.body().getSubsonicResponse().getNewestPodcasts().getEpisodes();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (PodcastEpisode episode : episodes) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(episode.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(episode.getTitle())
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PODCAST_EPISODE)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(episode.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(MusicUtil.getStreamUri(episode.getStreamId()))
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setPodcastEpisodesMetadata(episodes);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getInternetRadioStations() {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getInternetRadioClient()
|
||||
.getInternetRadioStations()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getInternetRadioStations() != null && response.body().getSubsonicResponse().getInternetRadioStations().getInternetRadioStations() != null) {
|
||||
|
||||
List<InternetRadioStation> radioStations = response.body().getSubsonicResponse().getInternetRadioStations().getInternetRadioStations();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (InternetRadioStation radioStation : radioStations) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(radioStation.getName())
|
||||
.setIsBrowsable(false)
|
||||
.setIsPlayable(true)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_RADIO_STATION)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(radioStation.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri(radioStation.getStreamUrl())
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
setInternetRadioStationsMetadata(radioStations);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getAlbumTracks(String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getAlbum(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null && response.body().getSubsonicResponse().getAlbum().getSongs() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getAlbum().getSongs();
|
||||
|
||||
setChildrenMetadata(tracks);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(tracks);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getArtistAlbum(String prefix, String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getArtist(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getArtist() != null && response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
|
||||
List<AlbumID3> albums = response.body().getSubsonicResponse().getArtist().getAlbums();
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (AlbumID3 album : albums) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setAlbumTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getPlaylistSongs(String id) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPlaylistClient()
|
||||
.getPlaylist(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getPlaylist() != null && response.body().getSubsonicResponse().getPlaylist().getEntries() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getPlaylist().getEntries();
|
||||
|
||||
setChildrenMetadata(tracks);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(tracks);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getMadeForYou(String id, int count) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getSimilarSongs2(id, count)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSimilarSongs2() != null && response.body().getSubsonicResponse().getSimilarSongs2().getSongs() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getSimilarSongs2().getSongs();
|
||||
|
||||
setChildrenMetadata(tracks);
|
||||
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(tracks);
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> search(String query, String albumPrefix, String artistPrefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, 20, 20, 20)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSearchResult3() != null) {
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
if (response.body().getSubsonicResponse().getSearchResult3().getArtists() != null) {
|
||||
for (ArtistID3 artist : response.body().getSubsonicResponse().getSearchResult3().getArtists()) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(artist.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(artist.getName())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(artistPrefix + artist.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body().getSubsonicResponse().getSearchResult3().getAlbums() != null) {
|
||||
for (AlbumID3 album : response.body().getSubsonicResponse().getSearchResult3().getAlbums()) {
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(album.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(album.getName())
|
||||
.setAlbumTitle(album.getName())
|
||||
.setArtist(album.getArtist())
|
||||
.setGenre(album.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_ALBUM)
|
||||
.setArtworkUri(artworkUri)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(albumPrefix + album.getId())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body().getSubsonicResponse().getSearchResult3().getSongs() != null) {
|
||||
List<Child> tracks = response.body().getSubsonicResponse().getSearchResult3().getSongs();
|
||||
setChildrenMetadata(tracks);
|
||||
mediaItems.addAll(MappingUtil.mapMediaItems(tracks));
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void setChildrenMetadata(List<Child> children) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||
|
||||
for (Child child : children) {
|
||||
SessionMediaItem sessionMediaItem = new SessionMediaItem(child);
|
||||
sessionMediaItem.setTimestamp(timestamp);
|
||||
sessionMediaItems.add(sessionMediaItem);
|
||||
}
|
||||
|
||||
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||
Thread thread = new Thread(insertAll);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void setPodcastEpisodesMetadata(List<PodcastEpisode> podcastEpisodes) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||
|
||||
for (PodcastEpisode podcastEpisode : podcastEpisodes) {
|
||||
SessionMediaItem sessionMediaItem = new SessionMediaItem(podcastEpisode);
|
||||
sessionMediaItem.setTimestamp(timestamp);
|
||||
sessionMediaItems.add(sessionMediaItem);
|
||||
}
|
||||
|
||||
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||
Thread thread = new Thread(insertAll);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void setInternetRadioStationsMetadata(List<InternetRadioStation> internetRadioStations) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
ArrayList<SessionMediaItem> sessionMediaItems = new ArrayList<>();
|
||||
|
||||
for (InternetRadioStation internetRadioStation : internetRadioStations) {
|
||||
SessionMediaItem sessionMediaItem = new SessionMediaItem(internetRadioStation);
|
||||
sessionMediaItem.setTimestamp(timestamp);
|
||||
sessionMediaItems.add(sessionMediaItem);
|
||||
}
|
||||
|
||||
InsertAllThreadSafe insertAll = new InsertAllThreadSafe(sessionMediaItemDao, sessionMediaItems);
|
||||
Thread thread = new Thread(insertAll);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public SessionMediaItem getSessionMediaItem(String id) {
|
||||
SessionMediaItem sessionMediaItem = null;
|
||||
|
||||
GetMediaItemThreadSafe getMediaItemThreadSafe = new GetMediaItemThreadSafe(sessionMediaItemDao, id);
|
||||
Thread thread = new Thread(getMediaItemThreadSafe);
|
||||
thread.start();
|
||||
|
||||
try {
|
||||
thread.join();
|
||||
sessionMediaItem = getMediaItemThreadSafe.getSessionMediaItem();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return sessionMediaItem;
|
||||
}
|
||||
|
||||
public List<MediaItem> getMetadatas(long timestamp) {
|
||||
List<MediaItem> mediaItems = Collections.emptyList();
|
||||
|
||||
GetMediaItemsThreadSafe getMediaItemsThreadSafe = new GetMediaItemsThreadSafe(sessionMediaItemDao, timestamp);
|
||||
Thread thread = new Thread(getMediaItemsThreadSafe);
|
||||
thread.start();
|
||||
|
||||
try {
|
||||
thread.join();
|
||||
mediaItems = getMediaItemsThreadSafe.getMediaItems();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return mediaItems;
|
||||
}
|
||||
|
||||
public void deleteMetadata() {
|
||||
DeleteAllThreadSafe delete = new DeleteAllThreadSafe(sessionMediaItemDao);
|
||||
Thread thread = new Thread(delete);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private static class GetMediaItemThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
private final String id;
|
||||
|
||||
private SessionMediaItem sessionMediaItem;
|
||||
|
||||
public GetMediaItemThreadSafe(SessionMediaItemDao sessionMediaItemDao, String id) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sessionMediaItem = sessionMediaItemDao.get(id);
|
||||
}
|
||||
|
||||
public SessionMediaItem getSessionMediaItem() {
|
||||
return sessionMediaItem;
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
private static class GetMediaItemsThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
private final Long timestamp;
|
||||
private final List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
public GetMediaItemsThreadSafe(SessionMediaItemDao sessionMediaItemDao, Long timestamp) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<SessionMediaItem> sessionMediaItems = sessionMediaItemDao.get(timestamp);
|
||||
sessionMediaItems.forEach(sessionMediaItem -> mediaItems.add(sessionMediaItem.getMediaItem()));
|
||||
}
|
||||
|
||||
public List<MediaItem> getMediaItems() {
|
||||
return mediaItems;
|
||||
}
|
||||
}
|
||||
|
||||
private static class InsertAllThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
private final List<SessionMediaItem> sessionMediaItems;
|
||||
|
||||
public InsertAllThreadSafe(SessionMediaItemDao sessionMediaItemDao, List<SessionMediaItem> sessionMediaItems) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
this.sessionMediaItems = sessionMediaItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sessionMediaItemDao.insertAll(sessionMediaItems);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeleteAllThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
|
||||
public DeleteAllThreadSafe(SessionMediaItemDao sessionMediaItemDao) {
|
||||
this.sessionMediaItemDao = sessionMediaItemDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
sessionMediaItemDao.deleteAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,11 @@ import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||
import com.cappielloantonio.tempo.database.dao.DownloadDao;
|
||||
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||
import com.cappielloantonio.tempo.model.Download;
|
||||
import com.cappielloantonio.tempo.model.Favorite;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DownloadRepository {
|
||||
@@ -15,6 +18,43 @@ public class DownloadRepository {
|
||||
return downloadDao.getAll();
|
||||
}
|
||||
|
||||
public Download getDownload(String id) {
|
||||
Download download = null;
|
||||
|
||||
GetDownloadThreadSafe getDownloadThreadSafe = new GetDownloadThreadSafe(downloadDao, id);
|
||||
Thread thread = new Thread(getDownloadThreadSafe);
|
||||
thread.start();
|
||||
|
||||
try {
|
||||
thread.join();
|
||||
download = getDownloadThreadSafe.getDownload();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return download;
|
||||
}
|
||||
|
||||
private static class GetDownloadThreadSafe implements Runnable {
|
||||
private final DownloadDao downloadDao;
|
||||
private final String id;
|
||||
private Download download;
|
||||
|
||||
public GetDownloadThreadSafe(DownloadDao downloadDao, String id) {
|
||||
this.downloadDao = downloadDao;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
download = downloadDao.getOne(id);
|
||||
}
|
||||
|
||||
public Download getDownload() {
|
||||
return download;
|
||||
}
|
||||
}
|
||||
|
||||
public void insert(Download download) {
|
||||
InsertThreadSafe insert = new InsertThreadSafe(downloadDao, download);
|
||||
Thread thread = new Thread(insert);
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||
import com.cappielloantonio.tempo.model.Favorite;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class FavoriteRepository {
|
||||
private final FavoriteDao favoriteDao = AppDatabase.getInstance().favoriteDao();
|
||||
|
||||
public void star(String id, String albumId, String artistId, StarCallback starCallback) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.star(id, albumId, artistId)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful()) {
|
||||
starCallback.onSuccess();
|
||||
} else {
|
||||
starCallback.onError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
starCallback.onError();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void unstar(String id, String albumId, String artistId, StarCallback starCallback) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.unstar(id, albumId, artistId)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful()) {
|
||||
starCallback.onSuccess();
|
||||
} else {
|
||||
starCallback.onError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
starCallback.onError();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<Favorite> getFavorites() {
|
||||
List<Favorite> favorites = new ArrayList<>();
|
||||
|
||||
GetAllThreadSafe getAllThreadSafe = new GetAllThreadSafe(favoriteDao);
|
||||
Thread thread = new Thread(getAllThreadSafe);
|
||||
thread.start();
|
||||
|
||||
try {
|
||||
thread.join();
|
||||
favorites = getAllThreadSafe.getFavorites();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return favorites;
|
||||
}
|
||||
|
||||
private static class GetAllThreadSafe implements Runnable {
|
||||
private final FavoriteDao favoriteDao;
|
||||
private List<Favorite> favorites = new ArrayList<>();
|
||||
|
||||
public GetAllThreadSafe(FavoriteDao favoriteDao) {
|
||||
this.favoriteDao = favoriteDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
favorites = favoriteDao.getAll();
|
||||
}
|
||||
|
||||
public List<Favorite> getFavorites() {
|
||||
return favorites;
|
||||
}
|
||||
}
|
||||
|
||||
public void starLater(String id, String albumId, String artistId, boolean toStar) {
|
||||
InsertThreadSafe insert = new InsertThreadSafe(favoriteDao, new Favorite(System.currentTimeMillis(), id, albumId, artistId, toStar));
|
||||
Thread thread = new Thread(insert);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private static class InsertThreadSafe implements Runnable {
|
||||
private final FavoriteDao favoriteDao;
|
||||
private final Favorite favorite;
|
||||
|
||||
public InsertThreadSafe(FavoriteDao favoriteDao, Favorite favorite) {
|
||||
this.favoriteDao = favoriteDao;
|
||||
this.favorite = favorite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
favoriteDao.insert(favorite);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(Favorite favorite) {
|
||||
DeleteThreadSafe delete = new DeleteThreadSafe(favoriteDao, favorite);
|
||||
Thread thread = new Thread(delete);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private static class DeleteThreadSafe implements Runnable {
|
||||
private final FavoriteDao favoriteDao;
|
||||
private final Favorite favorite;
|
||||
|
||||
public DeleteThreadSafe(FavoriteDao favoriteDao, Favorite favorite) {
|
||||
this.favoriteDao = favoriteDao;
|
||||
this.favorite = favorite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
favoriteDao.delete(favorite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Genre;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@@ -24,9 +26,14 @@ public class GenreRepository {
|
||||
.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().getGenres() != null) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse() != null && response.body().getSubsonicResponse().getGenres() != null) {
|
||||
List<Genre> genreList = response.body().getSubsonicResponse().getGenres().getGenres();
|
||||
|
||||
if (genreList == null || genreList.isEmpty()) {
|
||||
genres.setValue(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
|
||||
if (random) {
|
||||
Collections.shuffle(genreList);
|
||||
}
|
||||
@@ -34,7 +41,7 @@ public class GenreRepository {
|
||||
if (size != -1) {
|
||||
genres.setValue(genreList.subList(0, Math.min(size, genreList.size())));
|
||||
} else {
|
||||
genres.setValue(genreList);
|
||||
genres.setValue(genreList.stream().sorted(Comparator.comparing(Genre::getGenre)).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
@@ -95,12 +93,29 @@ public class PlaylistRepository {
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
Log.d("PLAYLIST", response.toString());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
Log.d("PLAYLIST", t.toString());
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updatePlaylist(String playlistId, String name, ArrayList<String> songsId) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPlaylistClient()
|
||||
.deletePlaylist(playlistId)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
createPlaylist(null, name, songsId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class PodcastRepository {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
Log.d(TAG, "onFailure()");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -133,4 +133,21 @@ public class PodcastRepository {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void downloadPodcastEpisode(String episodeId) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPodcastClient()
|
||||
.downloadPodcastEpisode(episodeId)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,12 @@ public class ScanRepository {
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull retrofit2.Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getScanStatus() != null) {
|
||||
callback.onSuccess(response.body().getSubsonicResponse().getScanStatus().isScanning(), response.body().getSubsonicResponse().getScanStatus().getCount());
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse() != null) {
|
||||
if (response.body().getSubsonicResponse().getError() != null) {
|
||||
callback.onError(new Exception(response.body().getSubsonicResponse().getError().getMessage()));
|
||||
} else if (response.body().getSubsonicResponse().getScanStatus() != null) {
|
||||
callback.onSuccess(response.body().getSubsonicResponse().getScanStatus().isScanning(), response.body().getSubsonicResponse().getScanStatus().getCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +40,12 @@ public class ScanRepository {
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull retrofit2.Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getScanStatus() != null) {
|
||||
callback.onSuccess(response.body().getSubsonicResponse().getScanStatus().isScanning(), response.body().getSubsonicResponse().getScanStatus().getCount());
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse() != null) {
|
||||
if (response.body().getSubsonicResponse().getError() != null) {
|
||||
callback.onError(new Exception(response.body().getSubsonicResponse().getError().getMessage()));
|
||||
} else if (response.body().getSubsonicResponse().getScanStatus() != null) {
|
||||
callback.onSuccess(response.body().getSubsonicResponse().getScanStatus().isScanning(), response.body().getSubsonicResponse().getScanStatus().getCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult2;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -24,7 +25,30 @@ import retrofit2.Response;
|
||||
public class SearchingRepository {
|
||||
private final RecentSearchDao recentSearchDao = AppDatabase.getInstance().recentSearchDao();
|
||||
|
||||
public MutableLiveData<SearchResult3> search(String query) {
|
||||
public MutableLiveData<SearchResult2> search2(String query) {
|
||||
MutableLiveData<SearchResult2> result = new MutableLiveData<>();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, 20, 20, 20)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
result.setValue(response.body().getSubsonicResponse().getSearchResult2());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public MutableLiveData<SearchResult3> search3(String query) {
|
||||
MutableLiveData<SearchResult3> result = new MutableLiveData<>();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
@@ -33,7 +57,9 @@ public class SearchingRepository {
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
result.setValue(response.body().getSubsonicResponse().getSearchResult3());
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
result.setValue(response.body().getSubsonicResponse().getSearchResult3());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,7 +82,7 @@ public class SearchingRepository {
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
List<String> newSuggestions = new ArrayList();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getSearchResult3() != null) {
|
||||
if (response.body().getSubsonicResponse().getSearchResult3().getArtists() != null) {
|
||||
for (ArtistID3 artistID3 : response.body().getSubsonicResponse().getSearchResult3().getArtists()) {
|
||||
newSuggestions.add(artistID3.getName());
|
||||
@@ -103,10 +129,10 @@ public class SearchingRepository {
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public List<String> getRecentSearchSuggestion(int limit) {
|
||||
public List<String> getRecentSearchSuggestion() {
|
||||
List<String> recent = new ArrayList<>();
|
||||
|
||||
RecentThreadSafe suggestionsThread = new RecentThreadSafe(recentSearchDao, limit);
|
||||
RecentThreadSafe suggestionsThread = new RecentThreadSafe(recentSearchDao);
|
||||
Thread thread = new Thread(suggestionsThread);
|
||||
thread.start();
|
||||
|
||||
@@ -152,17 +178,15 @@ public class SearchingRepository {
|
||||
|
||||
private static class RecentThreadSafe implements Runnable {
|
||||
private final RecentSearchDao recentSearchDao;
|
||||
private final int limit;
|
||||
private List<String> recent = new ArrayList<>();
|
||||
|
||||
public RecentThreadSafe(RecentSearchDao recentSearchDao, int limit) {
|
||||
public RecentThreadSafe(RecentSearchDao recentSearchDao) {
|
||||
this.recentSearchDao = recentSearchDao;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
recent = recentSearchDao.getRecent(limit);
|
||||
recent = recentSearchDao.getRecent();
|
||||
}
|
||||
|
||||
public List<String> getRecent() {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class SharingRepository {
|
||||
public MutableLiveData<List<Share>> getShares() {
|
||||
MutableLiveData<List<Share>> shares = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.getShares()
|
||||
.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().getShares() != null && response.body().getSubsonicResponse().getShares().getShares() != null) {
|
||||
shares.setValue(response.body().getSubsonicResponse().getShares().getShares());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return shares;
|
||||
}
|
||||
|
||||
public MutableLiveData<Share> createShare(String id, String description, Long expires) {
|
||||
MutableLiveData<Share> share = new MutableLiveData<>();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.createShare(id, description, expires)
|
||||
.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().getShares() != null && response.body().getSubsonicResponse().getShares().getShares() != null && response.body().getSubsonicResponse().getShares().getShares().get(0) != null) {
|
||||
share.setValue(response.body().getSubsonicResponse().getShares().getShares().get(0));
|
||||
} else {
|
||||
share.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
share.setValue(null);
|
||||
}
|
||||
});
|
||||
|
||||
return share;
|
||||
}
|
||||
|
||||
public void updateShare(String id, String description, Long expires) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.updateShare(id, description, expires)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteShare(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSharingClient()
|
||||
.deleteShare(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||
import com.cappielloantonio.tempo.database.dao.FavoriteDao;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
|
||||
@@ -86,7 +88,7 @@ public class SongRepository {
|
||||
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) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getRandomSongs() != null && response.body().getSubsonicResponse().getRandomSongs().getSongs() != null) {
|
||||
songs.addAll(response.body().getSubsonicResponse().getRandomSongs().getSongs());
|
||||
}
|
||||
|
||||
@@ -95,51 +97,17 @@ public class SongRepository {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
Log.d(TAG, "onFailure: ");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return randomSongsSample;
|
||||
}
|
||||
|
||||
public void scrobble(String id) {
|
||||
public void scrobble(String id, boolean submission) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.scrobble(id)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void star(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.star(id, null, null)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void unstar(String id) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getMediaAnnotationClient()
|
||||
.unstar(id, null, null)
|
||||
.scrobble(id, submission)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
@@ -173,8 +141,6 @@ public class SongRepository {
|
||||
public MutableLiveData<List<Child>> getSongsByGenre(String id, int page) {
|
||||
MutableLiveData<List<Child>> songsByGenre = new MutableLiveData<>();
|
||||
|
||||
Log.d(TAG, "onScrolled PAGE: " + page);
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getSongsByGenre(id, 100, 100 * page)
|
||||
@@ -239,7 +205,7 @@ public class SongRepository {
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
Log.d(TAG, "onFailure: ");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public class SystemRepository {
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull retrofit2.Response<ApiResponse> response) {
|
||||
if (response.body() != null) {
|
||||
if (response.body().getSubsonicResponse().getStatus().equals(ResponseStatus.FAILED)) {
|
||||
callback.onError(new Exception(response.body().getSubsonicResponse().getError().getCode().getValue() + " - " + response.body().getSubsonicResponse().getError().getMessage()));
|
||||
callback.onError(new Exception(response.body().getSubsonicResponse().getError().getCode() + " - " + response.body().getSubsonicResponse().getError().getMessage()));
|
||||
} else if (response.body().getSubsonicResponse().getStatus().equals(ResponseStatus.OK)) {
|
||||
String password = response.raw().request().url().queryParameter("p");
|
||||
String token = response.raw().request().url().queryParameter("t");
|
||||
|
||||
@@ -31,9 +31,10 @@ public class DownloaderManager {
|
||||
|
||||
private final Context context;
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final HashMap<String, Download> downloads;
|
||||
private final DownloadIndex downloadIndex;
|
||||
|
||||
private static HashMap<String, Download> downloads;
|
||||
|
||||
public DownloaderManager(Context context, DataSource.Factory dataSourceFactory, DownloadManager downloadManager) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
@@ -61,22 +62,16 @@ public class DownloaderManager {
|
||||
}
|
||||
|
||||
public boolean isDownloaded(MediaItem mediaItem) {
|
||||
@Nullable Download download = downloads.get(mediaItem.mediaId);
|
||||
return download != null && download.state != Download.STATE_FAILED;
|
||||
return isDownloaded(mediaItem.mediaId);
|
||||
}
|
||||
|
||||
public boolean areDownloaded(List<MediaItem> mediaItems) {
|
||||
for (MediaItem mediaItem : mediaItems) {
|
||||
@Nullable Download download = downloads.get(mediaItem.mediaId);
|
||||
if (download != null && download.state != Download.STATE_FAILED) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return mediaItems.stream().anyMatch(this::isDownloaded);
|
||||
}
|
||||
|
||||
public void download(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||
download.setDownloadUri(mediaItem.requestMetadata.mediaUri.toString());
|
||||
|
||||
DownloadService.sendAddDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem), false);
|
||||
insertDatabase(download);
|
||||
}
|
||||
@@ -89,6 +84,8 @@ public class DownloaderManager {
|
||||
|
||||
public void remove(MediaItem mediaItem, com.cappielloantonio.tempo.model.Download download) {
|
||||
DownloadService.sendRemoveDownload(context, DownloaderService.class, buildDownloadRequest(mediaItem).id, false);
|
||||
deleteDatabase(download.getId());
|
||||
downloads.remove(download.getId());
|
||||
}
|
||||
|
||||
public void remove(List<MediaItem> mediaItems, List<com.cappielloantonio.tempo.model.Download> downloads) {
|
||||
@@ -97,6 +94,12 @@ public class DownloaderManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAll() {
|
||||
DownloadService.sendRemoveAllDownloads(context, DownloaderService.class, false);
|
||||
deleteAllDatabase();
|
||||
DownloadUtil.eraseDownloadFolder(context);
|
||||
}
|
||||
|
||||
private void loadDownloads() {
|
||||
try (DownloadCursor loadedDownloads = downloadIndex.getDownloads()) {
|
||||
while (loadedDownloads.moveToNext()) {
|
||||
@@ -108,19 +111,38 @@ public class DownloaderManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDownloadNotificationMessage(String id) {
|
||||
com.cappielloantonio.tempo.model.Download download = getDownloadRepository().getDownload(id);
|
||||
return download != null ? download.getTitle() : null;
|
||||
}
|
||||
|
||||
public static void updateRequestDownload(Download download) {
|
||||
updateDatabase(download.request.id);
|
||||
downloads.put(download.request.id, download);
|
||||
}
|
||||
|
||||
public static void removeRequestDownload(Download download) {
|
||||
deleteDatabase(download.request.id);
|
||||
downloads.remove(download.request.id);
|
||||
}
|
||||
|
||||
private static DownloadRepository getDownloadRepository() {
|
||||
return new DownloadRepository();
|
||||
}
|
||||
|
||||
public static void insertDatabase(com.cappielloantonio.tempo.model.Download download) {
|
||||
private static void insertDatabase(com.cappielloantonio.tempo.model.Download download) {
|
||||
getDownloadRepository().insert(download);
|
||||
}
|
||||
|
||||
public static void deleteDatabase(String id) {
|
||||
private static void deleteDatabase(String id) {
|
||||
getDownloadRepository().delete(id);
|
||||
}
|
||||
|
||||
public static void updateDatabase(String id) {
|
||||
private static void deleteAllDatabase() {
|
||||
getDownloadRepository().deleteAll();
|
||||
}
|
||||
|
||||
private static void updateDatabase(String id) {
|
||||
getDownloadRepository().update(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.util.NotificationUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.offline.Download;
|
||||
import androidx.media3.exoplayer.offline.DownloadManager;
|
||||
import androidx.media3.exoplayer.offline.DownloadNotificationHelper;
|
||||
@@ -55,12 +54,37 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||
private final Context context;
|
||||
private final DownloadNotificationHelper notificationHelper;
|
||||
|
||||
private final Notification successfulDownloadGroupNotification;
|
||||
private final Notification failedDownloadGroupNotification;
|
||||
|
||||
private final int successfulDownloadGroupNotificationId;
|
||||
private final int failedDownloadGroupNotificationId;
|
||||
|
||||
private int nextNotificationId;
|
||||
|
||||
public TerminalStateNotificationHelper(Context context, DownloadNotificationHelper notificationHelper, int firstNotificationId) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.notificationHelper = notificationHelper;
|
||||
nextNotificationId = firstNotificationId;
|
||||
|
||||
successfulDownloadGroupNotification = DownloadUtil.buildGroupSummaryNotification(
|
||||
this.context,
|
||||
DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID,
|
||||
DownloadUtil.DOWNLOAD_NOTIFICATION_SUCCESSFUL_GROUP,
|
||||
R.drawable.ic_check_circle,
|
||||
"Downloads completed"
|
||||
);
|
||||
|
||||
failedDownloadGroupNotification = DownloadUtil.buildGroupSummaryNotification(
|
||||
this.context,
|
||||
DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID,
|
||||
DownloadUtil.DOWNLOAD_NOTIFICATION_FAILED_GROUP,
|
||||
R.drawable.ic_error,
|
||||
"Downloads failed"
|
||||
);
|
||||
|
||||
successfulDownloadGroupNotificationId = nextNotificationId++;
|
||||
failedDownloadGroupNotificationId = nextNotificationId++;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,10 +92,14 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||
Notification notification;
|
||||
|
||||
if (download.state == Download.STATE_COMPLETED) {
|
||||
notification = notificationHelper.buildDownloadCompletedNotification(context, R.drawable.ic_check_circle, null, Util.fromUtf8Bytes(download.request.data));
|
||||
DownloaderManager.updateDatabase(download.request.id);
|
||||
notification = notificationHelper.buildDownloadCompletedNotification(context, R.drawable.ic_check_circle, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||
notification = Notification.Builder.recoverBuilder(context, notification).setGroup(DownloadUtil.DOWNLOAD_NOTIFICATION_SUCCESSFUL_GROUP).build();
|
||||
NotificationUtil.setNotification(this.context, successfulDownloadGroupNotificationId, successfulDownloadGroupNotification);
|
||||
DownloaderManager.updateRequestDownload(download);
|
||||
} else if (download.state == Download.STATE_FAILED) {
|
||||
notification = notificationHelper.buildDownloadFailedNotification(context, R.drawable.ic_error, null, Util.fromUtf8Bytes(download.request.data));
|
||||
notification = notificationHelper.buildDownloadFailedNotification(context, R.drawable.ic_error, null, DownloaderManager.getDownloadNotificationMessage(download.request.id));
|
||||
notification = Notification.Builder.recoverBuilder(context, notification).setGroup(DownloadUtil.DOWNLOAD_NOTIFICATION_FAILED_GROUP).build();
|
||||
NotificationUtil.setNotification(this.context, failedDownloadGroupNotificationId, failedDownloadGroupNotification);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -81,7 +109,7 @@ public class DownloaderService extends androidx.media3.exoplayer.offline.Downloa
|
||||
|
||||
@Override
|
||||
public void onDownloadRemoved(@NonNull DownloadManager downloadManager, Download download) {
|
||||
DownloaderManager.deleteDatabase(download.request.id);
|
||||
DownloaderManager.removeRequestDownload(download);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
@@ -204,6 +205,22 @@ public class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void shuffle(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, List<Child> media, int startIndex, int endIndex) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().removeMediaItems(startIndex, endIndex + 1);
|
||||
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1));
|
||||
swapDatabase(media);
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
public static void swap(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, List<Child> media, int from, int to) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
@@ -238,6 +255,21 @@ public class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeRange(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, List<Child> media, int fromItem, int toItem) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().removeMediaItems(fromItem, toItem);
|
||||
removeRangeDatabase(media, fromItem, toItem);
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
public static void getCurrentIndex(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, MediaIndexCallback callback) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
@@ -261,9 +293,9 @@ public class MediaManager {
|
||||
getQueueRepository().setPlayingPausedTimestamp(mediaItem.mediaId, ms);
|
||||
}
|
||||
|
||||
public static void scrobble(MediaItem mediaItem) {
|
||||
if (mediaItem != null) {
|
||||
getSongRepository().scrobble(mediaItem.mediaMetadata.extras.getString("id"));
|
||||
public static void scrobble(MediaItem mediaItem, boolean submission) {
|
||||
if (mediaItem != null && Preferences.isScrobblingEnabled()) {
|
||||
getSongRepository().scrobble(mediaItem.mediaMetadata.extras.getString("id"), submission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,6 +336,14 @@ public class MediaManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeRangeDatabase(List<Child> media, int fromItem, int toItem) {
|
||||
List<Child> toRemove = media.subList(fromItem, toItem);
|
||||
|
||||
media.removeAll(toRemove);
|
||||
|
||||
getQueueRepository().insertAll(media, true, 0);
|
||||
}
|
||||
|
||||
public static void clearDatabase() {
|
||||
getQueueRepository().deleteAll();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ class RetrofitClient(subsonic: Subsonic) {
|
||||
init {
|
||||
retrofit = Retrofit.Builder()
|
||||
.baseUrl(subsonic.url)
|
||||
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create()))
|
||||
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
|
||||
.client(getOkHttpClient())
|
||||
.build()
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.cappielloantonio.tempo.subsonic.api.mediaretrieval.MediaRetrievalClie
|
||||
import com.cappielloantonio.tempo.subsonic.api.playlist.PlaylistClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.podcast.PodcastClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.searching.SearchingClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.sharing.SharingClient;
|
||||
import com.cappielloantonio.tempo.subsonic.api.system.SystemClient;
|
||||
import com.cappielloantonio.tempo.subsonic.base.Version;
|
||||
|
||||
@@ -33,6 +34,7 @@ public class Subsonic {
|
||||
private MediaLibraryScanningClient mediaLibraryScanningClient;
|
||||
private BookmarksClient bookmarksClient;
|
||||
private InternetRadioClient internetRadioClient;
|
||||
private SharingClient sharingClient;
|
||||
|
||||
public Subsonic(SubsonicPreferences preferences) {
|
||||
this.preferences = preferences;
|
||||
@@ -119,6 +121,13 @@ public class Subsonic {
|
||||
return internetRadioClient;
|
||||
}
|
||||
|
||||
public SharingClient getSharingClient() {
|
||||
if (sharingClient == null) {
|
||||
sharingClient = new SharingClient(this);
|
||||
}
|
||||
return sharingClient;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
String url = preferences.getServerUrl() + "/rest/";
|
||||
return url.replace("//rest", "/rest");
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import retrofit2.Call;
|
||||
|
||||
public class MediaAnnotationClient {
|
||||
private static final String TAG = "BrowsingClient";
|
||||
private static final String TAG = "MediaAnnotationClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final MediaAnnotationService mediaAnnotationService;
|
||||
@@ -34,8 +34,8 @@ public class MediaAnnotationClient {
|
||||
return mediaAnnotationService.setRating(subsonic.getParams(), id, rating);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> scrobble(String id) {
|
||||
public Call<ApiResponse> scrobble(String id, boolean submission) {
|
||||
Log.d(TAG, "scrobble()");
|
||||
return mediaAnnotationService.scrobble(subsonic.getParams(), id);
|
||||
return mediaAnnotationService.scrobble(subsonic.getParams(), id, submission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@ public interface MediaAnnotationService {
|
||||
Call<ApiResponse> setRating(@QueryMap Map<String, String> params, @Query("id") String id, @Query("rating") int rating);
|
||||
|
||||
@GET("scrobble")
|
||||
Call<ApiResponse> scrobble(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||
Call<ApiResponse> scrobble(@QueryMap Map<String, String> params, @Query("id") String id, @Query("submission") Boolean submission);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import retrofit2.Call;
|
||||
|
||||
public class MediaLibraryScanningClient {
|
||||
private static final String TAG = "SystemClient";
|
||||
private static final String TAG = "MediaLibraryScanningClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final MediaLibraryScanningService mediaLibraryScanningService;
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import retrofit2.Call;
|
||||
|
||||
public class PodcastClient {
|
||||
private static final String TAG = "SystemClient";
|
||||
private static final String TAG = "PodcastClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final PodcastService podcastService;
|
||||
@@ -48,4 +48,9 @@ public class PodcastClient {
|
||||
Log.d(TAG, "deletePodcastEpisode()");
|
||||
return podcastService.deletePodcastEpisode(subsonic.getParams(), episodeId);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> downloadPodcastEpisode(String episodeId) {
|
||||
Log.d(TAG, "downloadPodcastEpisode()");
|
||||
return podcastService.downloadPodcastEpisode(subsonic.getParams(), episodeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,7 @@ public interface PodcastService {
|
||||
|
||||
@GET("deletePodcastEpisode")
|
||||
Call<ApiResponse> deletePodcastEpisode(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||
|
||||
@GET("downloadPodcastEpisode")
|
||||
Call<ApiResponse> downloadPodcastEpisode(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.cappielloantonio.tempo.subsonic.api.sharing;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.cappielloantonio.tempo.subsonic.RetrofitClient;
|
||||
import com.cappielloantonio.tempo.subsonic.Subsonic;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
|
||||
import retrofit2.Call;
|
||||
|
||||
public class SharingClient {
|
||||
private static final String TAG = "BrowsingClient";
|
||||
|
||||
private final Subsonic subsonic;
|
||||
private final SharingService sharingService;
|
||||
|
||||
public SharingClient(Subsonic subsonic) {
|
||||
this.subsonic = subsonic;
|
||||
this.sharingService = new RetrofitClient(subsonic).getRetrofit().create(SharingService.class);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> getShares() {
|
||||
Log.d(TAG, "getShares()");
|
||||
return sharingService.getShares(subsonic.getParams());
|
||||
}
|
||||
|
||||
public Call<ApiResponse> createShare(String id, String description, Long expires) {
|
||||
Log.d(TAG, "createShare()");
|
||||
return sharingService.createShare(subsonic.getParams(), id, description, expires);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> updateShare(String id, String description, Long expires) {
|
||||
Log.d(TAG, "updateShare()");
|
||||
return sharingService.updateShare(subsonic.getParams(), id, description, expires);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> deleteShare(String id) {
|
||||
Log.d(TAG, "deleteShare()");
|
||||
return sharingService.deleteShare(subsonic.getParams(), id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.cappielloantonio.tempo.subsonic.api.sharing;
|
||||
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
import retrofit2.http.QueryMap;
|
||||
|
||||
public interface SharingService {
|
||||
@GET("getShares")
|
||||
Call<ApiResponse> getShares(@QueryMap Map<String, String> params);
|
||||
|
||||
@GET("createShare")
|
||||
Call<ApiResponse> createShare(@QueryMap Map<String, String> params, @Query("id") String id, @Query("description") String description, @Query("expires") Long expires);
|
||||
|
||||
@GET("updateShare")
|
||||
Call<ApiResponse> updateShare(@QueryMap Map<String, String> params, @Query("id") String id, @Query("description") String description, @Query("expires") Long expires);
|
||||
|
||||
@GET("deleteShare")
|
||||
Call<ApiResponse> deleteShare(@QueryMap Map<String, String> params, @Query("id") String id);
|
||||
}
|
||||
@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
|
||||
@Keep
|
||||
class ApiResponse {
|
||||
@SerializedName("subsonic-response")
|
||||
var subsonicResponse: SubsonicResponse? = null
|
||||
lateinit var subsonicResponse: SubsonicResponse
|
||||
}
|
||||
@@ -4,6 +4,6 @@ import androidx.annotation.Keep
|
||||
|
||||
@Keep
|
||||
class Error {
|
||||
var code: ErrorCode? = null
|
||||
var code: Int? = null
|
||||
var message: String? = null
|
||||
}
|
||||
@@ -14,6 +14,7 @@ class PodcastChannel : Parcelable {
|
||||
var url: String? = null
|
||||
var title: String? = null
|
||||
var description: String? = null
|
||||
@SerializedName("coverArt")
|
||||
var coverArtId: String? = null
|
||||
var originalImageUrl: String? = null
|
||||
var status: String? = null
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.cappielloantonio.tempo.subsonic.models
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import androidx.room.ColumnInfo
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
@@ -13,12 +12,10 @@ class PodcastEpisode : Parcelable {
|
||||
var id: String? = null
|
||||
@SerializedName("parent")
|
||||
var parentId: String? = null
|
||||
@SerializedName("isDir")
|
||||
var isDir = false
|
||||
var title: String? = null
|
||||
var album: String? = null
|
||||
var artist: String? = null
|
||||
var track: Int? = null
|
||||
var year: Int? = null
|
||||
var genre: String? = null
|
||||
@SerializedName("coverArt")
|
||||
@@ -26,28 +23,14 @@ class PodcastEpisode : Parcelable {
|
||||
var size: Long? = null
|
||||
var contentType: String? = null
|
||||
var suffix: String? = null
|
||||
var transcodedContentType: String? = null
|
||||
var transcodedSuffix: String? = null
|
||||
var duration: Int? = null
|
||||
@ColumnInfo("bitrate")
|
||||
@SerializedName("bitRate")
|
||||
var bitrate: Int? = null
|
||||
var path: String? = null
|
||||
@ColumnInfo(name = "is_video")
|
||||
@SerializedName("isVideo")
|
||||
var isVideo: Boolean = false
|
||||
var userRating: Int? = null
|
||||
var averageRating: Double? = null
|
||||
var playCount: Long? = null
|
||||
var discNumber: Int? = null
|
||||
var created: Date? = null
|
||||
var starred: Date? = null
|
||||
var albumId: String? = null
|
||||
var artistId: String? = null
|
||||
var type: String? = null
|
||||
var bookmarkPosition: Long? = null
|
||||
var originalWidth: Int? = null
|
||||
var originalHeight: Int? = null
|
||||
var streamId: String? = null
|
||||
var channelId: String? = null
|
||||
var description: String? = null
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package com.cappielloantonio.tempo.subsonic.models
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.*
|
||||
|
||||
@Keep
|
||||
class Share {
|
||||
@Parcelize
|
||||
class Share : Parcelable {
|
||||
@SerializedName("entry")
|
||||
var entries: List<Child>? = null
|
||||
var id: String? = null
|
||||
var url: String? = null
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.cappielloantonio.tempo.subsonic.models
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
class Shares {
|
||||
@SerializedName("share")
|
||||
var shares: List<Share>? = null
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.cappielloantonio.tempo.ui.activity;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
@@ -30,6 +31,7 @@ import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.MainViewModel;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -50,10 +52,10 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
SplashScreen.installSplashScreen(this);
|
||||
DynamicColors.applyToActivityIfAvailable(this);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@@ -92,7 +94,7 @@ public class MainActivity extends BaseActivity {
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED)
|
||||
collapseBottomSheet();
|
||||
collapseBottomSheetDelayed();
|
||||
else
|
||||
super.onBackPressed();
|
||||
}
|
||||
@@ -116,7 +118,7 @@ public class MainActivity extends BaseActivity {
|
||||
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback);
|
||||
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
||||
|
||||
setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||
checkBottomSheetAfterStateChanged();
|
||||
}
|
||||
|
||||
public void setBottomSheetInPeek(Boolean isVisible) {
|
||||
@@ -135,12 +137,22 @@ public class MainActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
public void collapseBottomSheet() {
|
||||
private void checkBottomSheetAfterStateChanged() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
public void collapseBottomSheetDelayed() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
public void expandBottomSheet() {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
|
||||
public void setBottomSheetDraggableState(Boolean isDraggable) {
|
||||
bottomSheetBehavior.setDraggable(isDraggable);
|
||||
}
|
||||
@@ -155,7 +167,7 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
switch (state) {
|
||||
case BottomSheetBehavior.STATE_HIDDEN:
|
||||
hideMusicSession();
|
||||
resetMusicSession();
|
||||
break;
|
||||
case BottomSheetBehavior.STATE_COLLAPSED:
|
||||
if (playerBottomSheetFragment != null)
|
||||
@@ -247,6 +259,7 @@ public class MainActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
private void goToLogin() {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
setBottomNavigationBarVisibility(false);
|
||||
setBottomSheetVisibility(false);
|
||||
|
||||
@@ -333,7 +346,9 @@ public class MainActivity extends BaseActivity {
|
||||
private void checkConnectionType() {
|
||||
if (Preferences.isWifiOnly()) {
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivityManager.getActiveNetworkInfo().getType() != ConnectivityManager.TYPE_WIFI) {
|
||||
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
|
||||
if (networkInfo != null && networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
|
||||
ConnectionAlertDialog dialog = new ConnectionAlertDialog();
|
||||
dialog.show(getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
@@ -2,15 +2,13 @@ package com.cappielloantonio.tempo.ui.activity.base;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@@ -19,11 +17,11 @@ import androidx.media3.exoplayer.offline.DownloadService;
|
||||
import androidx.media3.session.MediaBrowser;
|
||||
import androidx.media3.session.SessionToken;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.service.DownloaderService;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
import com.google.android.gms.cast.framework.CastContext;
|
||||
import com.cappielloantonio.tempo.ui.dialog.BatteryOptimizationDialog;
|
||||
import com.cappielloantonio.tempo.util.Flavors;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.elevation.SurfaceColors;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
@@ -36,8 +34,11 @@ public class BaseActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
initializeCastContext();
|
||||
Flavors.initializeCastContext(this);
|
||||
initializeDownloader();
|
||||
checkBatteryOptimization();
|
||||
checkPermission();
|
||||
checkAlwaysOnDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,13 +48,6 @@ public class BaseActivity extends AppCompatActivity {
|
||||
initializeBrowser();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
checkBatteryOptimization();
|
||||
checkPermission();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
releaseBrowser();
|
||||
@@ -61,7 +55,7 @@ public class BaseActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void checkBatteryOptimization() {
|
||||
if (detectBatteryOptimization()) {
|
||||
if (detectBatteryOptimization() && Preferences.askForOptimization()) {
|
||||
showBatteryOptimizationDialog();
|
||||
}
|
||||
}
|
||||
@@ -74,6 +68,12 @@ public class BaseActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAlwaysOnDisplay() {
|
||||
if (Preferences.isDisplayAlwaysOn()) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean detectBatteryOptimization() {
|
||||
String packageName = getPackageName();
|
||||
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
||||
@@ -81,19 +81,8 @@ public class BaseActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void showBatteryOptimizationDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(R.string.activity_battery_optimizations_summary)
|
||||
.setTitle(R.string.activity_battery_optimizations_title)
|
||||
.setNegativeButton(R.string.activity_negative_button, null)
|
||||
.setPositiveButton(R.string.activity_neutral_button, (dialog, id) -> openPowerSettings())
|
||||
.show();
|
||||
}
|
||||
|
||||
private void openPowerSettings() {
|
||||
Intent intent = new Intent();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
|
||||
startActivity(intent);
|
||||
BatteryOptimizationDialog dialog = new BatteryOptimizationDialog();
|
||||
dialog.show(getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
private void initializeBrowser() {
|
||||
@@ -116,11 +105,8 @@ public class BaseActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeCastContext() {
|
||||
if (UIUtil.isCastApiAvailable(this)) CastContext.getSharedInstance(this);
|
||||
}
|
||||
|
||||
private void setNavigationBarColor() {
|
||||
getWindow().setNavigationBarColor(SurfaceColors.getColorForElevation(this, 10));
|
||||
getWindow().setNavigationBarColor(SurfaceColors.getColorForElevation(this, 8));
|
||||
getWindow().setStatusBarColor(SurfaceColors.getColorForElevation(this, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(album.getArtist()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||
.build()
|
||||
.into(holder.item.albumCoverImageView);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class AlbumArtistPageOrSimilarAdapter extends RecyclerView.Adapter<AlbumA
|
||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(album.getArtist()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||
.build()
|
||||
.into(holder.item.artistPageAlbumCoverImageView);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.List;
|
||||
|
||||
public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAdapter.ViewHolder> implements Filterable {
|
||||
private final ClickCallback click;
|
||||
private String currentFilter;
|
||||
|
||||
private final Filter filtering = new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
@@ -32,6 +34,7 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
filteredList.addAll(albumsFull);
|
||||
} else {
|
||||
String filterPattern = constraint.toString().toLowerCase().trim();
|
||||
currentFilter = filterPattern;
|
||||
|
||||
for (AlbumID3 item : albumsFull) {
|
||||
if (item.getName().toLowerCase().contains(filterPattern)) {
|
||||
@@ -48,8 +51,7 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
albums.clear();
|
||||
albums.addAll((List) results.values);
|
||||
albums = (List<AlbumID3>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
@@ -60,6 +62,8 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
public AlbumCatalogueAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.albums = Collections.emptyList();
|
||||
this.albumsFull = Collections.emptyList();
|
||||
this.currentFilter = "";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -77,7 +81,7 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(album.getArtist()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||
.build()
|
||||
.into(holder.item.albumCatalogueCoverImageView);
|
||||
}
|
||||
@@ -92,9 +96,8 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
}
|
||||
|
||||
public void setItems(List<AlbumID3> albums) {
|
||||
this.albums = albums;
|
||||
this.albumsFull = new ArrayList<>(albums);
|
||||
notifyDataSetChanged();
|
||||
filtering.filter(currentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,4 +165,4 @@ public class AlbumCatalogueAdapter extends RecyclerView.Adapter<AlbumCatalogueAd
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class AlbumHorizontalAdapter extends RecyclerView.Adapter<AlbumHorizontal
|
||||
holder.item.albumArtistTextView.setText(MusicUtil.getReadableString(album.getArtist()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), album.getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||
.build()
|
||||
.into(holder.item.albumCoverImageView);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class ArtistAdapter extends RecyclerView.Adapter<ArtistAdapter.ViewHolder
|
||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(artist.getName()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
||||
.build()
|
||||
.into(holder.item.artistCoverImageView);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ArtistCatalogueAdapter extends RecyclerView.Adapter<ArtistCatalogue
|
||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(artist.getName()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
||||
.build()
|
||||
.into(holder.item.artistCatalogueCoverImageView);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ArtistHorizontalAdapter extends RecyclerView.Adapter<ArtistHorizont
|
||||
}
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
||||
.build()
|
||||
.into(holder.item.artistCoverImageView);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ArtistSimilarAdapter extends RecyclerView.Adapter<ArtistSimilarAdap
|
||||
holder.item.artistNameLabel.setText(MusicUtil.getReadableString(artist.getName()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), artist.getCoverArtId(), CustomGlideRequest.ResourceType.Artist)
|
||||
.build()
|
||||
.into(holder.item.similarArtistCoverImageView);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter<DiscoverSongAdapte
|
||||
holder.item.albumDiscoverSongLabel.setText(MusicUtil.getReadableString(song.getAlbum()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.discoverSongCoverImageView);
|
||||
}
|
||||
|
||||
@@ -11,25 +11,36 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.ItemHorizontalDownloadBinding;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@UnstableApi
|
||||
public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHorizontalAdapter.ViewHolder> {
|
||||
private final ClickCallback click;
|
||||
|
||||
private String view;
|
||||
private String filterKey;
|
||||
private String filterValue;
|
||||
|
||||
private List<Child> songs;
|
||||
private List<Child> shuffling;
|
||||
private List<Child> grouped;
|
||||
|
||||
public DownloadHorizontalAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.view = Constants.DOWNLOAD_TYPE_TRACK;
|
||||
this.songs = Collections.emptyList();
|
||||
this.grouped = Collections.emptyList();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -41,31 +52,48 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
Child song = songs.get(position);
|
||||
|
||||
holder.item.downloadedSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||
holder.item.downloadedSongArtistTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
||||
holder.item.downloadedSongAlbumTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
|
||||
|
||||
if (position > 0 && songs.get(position - 1) != null && !Objects.equals(songs.get(position - 1).getAlbum(), songs.get(position).getAlbum())) {
|
||||
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
|
||||
} else {
|
||||
if (position > 0) holder.item.divider.setVisibility(View.GONE);
|
||||
switch (view) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
initTrackLayout(holder, position);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
initAlbumLayout(holder, position);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
initArtistLayout(holder, position);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
initGenreLayout(holder, position);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
initYearLayout(holder, position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return songs.size();
|
||||
return grouped.size();
|
||||
}
|
||||
|
||||
public void setItems(List<Child> songs) {
|
||||
public void setItems(String view, String filterKey, String filterValue, List<Child> songs) {
|
||||
this.view = filterValue != null ? view : filterKey;
|
||||
this.filterKey = filterKey;
|
||||
this.filterValue = filterValue;
|
||||
|
||||
this.songs = songs;
|
||||
this.grouped = groupSong(songs);
|
||||
this.shuffling = shufflingSong(new ArrayList<>(songs));
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Child getItem(int id) {
|
||||
return songs.get(id);
|
||||
return grouped.get(id);
|
||||
}
|
||||
|
||||
public List<Child> getShuffling() {
|
||||
return shuffling;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,6 +106,166 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
return position;
|
||||
}
|
||||
|
||||
private List<Child> groupSong(List<Child> songs) {
|
||||
switch (view) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getId())).filter(Util.distinctByKey(Child::getId)).collect(Collectors.toList()));
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getAlbumId())).filter(Util.distinctByKey(Child::getAlbumId)).collect(Collectors.toList()));
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getArtistId())).filter(Util.distinctByKey(Child::getArtistId)).collect(Collectors.toList()));
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getGenre())).filter(Util.distinctByKey(Child::getGenre)).collect(Collectors.toList()));
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
return filterSong(filterKey, filterValue, songs.stream().filter(song -> Objects.nonNull(song.getYear())).filter(Util.distinctByKey(Child::getYear)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<Child> filterSong(String filterKey, String filterValue, List<Child> songs) {
|
||||
if (filterValue != null) {
|
||||
switch (filterKey) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
return songs.stream().filter(child -> child.getId().equals(filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getAlbumId(), filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getGenre(), filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getArtistId(), filterValue)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
return songs;
|
||||
}
|
||||
|
||||
private List<Child> shufflingSong(List<Child> songs) {
|
||||
if (filterValue == null) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
switch (filterKey) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
return songs.stream().filter(child -> child.getId().equals(filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getAlbumId(), filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getGenre(), filterValue)).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).collect(Collectors.toList());
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
return songs.stream().filter(child -> Objects.equals(child.getArtistId(), filterValue)).collect(Collectors.toList());
|
||||
default:
|
||||
return songs;
|
||||
}
|
||||
}
|
||||
|
||||
private String countSong(String filterKey, String filterValue, List<Child> songs) {
|
||||
if (filterValue != null) {
|
||||
switch (filterKey) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
return String.valueOf(songs.stream().filter(child -> child.getId().equals(filterValue)).count());
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getAlbumId(), filterValue)).count());
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getGenre(), filterValue)).count());
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getYear(), Integer.valueOf(filterValue))).count());
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
return String.valueOf(songs.stream().filter(child -> Objects.equals(child.getArtistId(), filterValue)).count());
|
||||
}
|
||||
}
|
||||
|
||||
return "0";
|
||||
}
|
||||
|
||||
private void initTrackLayout(ViewHolder holder, int position) {
|
||||
Child song = grouped.get(position);
|
||||
|
||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
||||
holder.item.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.itemCoverImageView);
|
||||
|
||||
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
|
||||
holder.item.downloadedItemMoreButton.setVisibility(View.VISIBLE);
|
||||
holder.item.divider.setVisibility(View.VISIBLE);
|
||||
|
||||
if (position > 0 && grouped.get(position - 1) != null && !Objects.equals(grouped.get(position - 1).getAlbum(), grouped.get(position).getAlbum())) {
|
||||
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
|
||||
} else {
|
||||
if (position > 0) holder.item.divider.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAlbumLayout(ViewHolder holder, int position) {
|
||||
Child song = grouped.get(position);
|
||||
|
||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getAlbum()));
|
||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ALBUM, song.getAlbumId(), songs)));
|
||||
holder.item.downloadedItemPreTextView.setText(MusicUtil.getReadableString(song.getArtist()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.itemCoverImageView);
|
||||
|
||||
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
|
||||
holder.item.downloadedItemMoreButton.setVisibility(View.VISIBLE);
|
||||
holder.item.divider.setVisibility(View.VISIBLE);
|
||||
|
||||
if (position > 0 && grouped.get(position - 1) != null && !Objects.equals(grouped.get(position - 1).getArtist(), grouped.get(position).getArtist())) {
|
||||
holder.item.divider.setPadding(0, (int) holder.itemView.getContext().getResources().getDimension(R.dimen.downloaded_item_padding), 0, 0);
|
||||
} else {
|
||||
if (position > 0) holder.item.divider.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void initArtistLayout(ViewHolder holder, int position) {
|
||||
Child song = grouped.get(position);
|
||||
|
||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getArtist()));
|
||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_ARTIST, song.getArtistId(), songs)));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.itemCoverImageView);
|
||||
|
||||
holder.item.itemCoverImageView.setVisibility(View.VISIBLE);
|
||||
holder.item.downloadedItemMoreButton.setVisibility(View.VISIBLE);
|
||||
holder.item.divider.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void initGenreLayout(ViewHolder holder, int position) {
|
||||
Child song = grouped.get(position);
|
||||
|
||||
holder.item.downloadedItemTitleTextView.setText(MusicUtil.getReadableString(song.getGenre()));
|
||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_GENRE, song.getGenre(), songs)));
|
||||
|
||||
holder.item.itemCoverImageView.setVisibility(View.GONE);
|
||||
holder.item.downloadedItemMoreButton.setVisibility(View.VISIBLE);
|
||||
holder.item.divider.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void initYearLayout(ViewHolder holder, int position) {
|
||||
Child song = grouped.get(position);
|
||||
|
||||
holder.item.downloadedItemTitleTextView.setText(String.valueOf(song.getYear()));
|
||||
holder.item.downloadedItemSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.download_item_single_subtitle_formatter, countSong(Constants.DOWNLOAD_TYPE_YEAR, song.getYear().toString(), songs)));
|
||||
|
||||
holder.item.itemCoverImageView.setVisibility(View.GONE);
|
||||
holder.item.downloadedItemMoreButton.setVisibility(View.VISIBLE);
|
||||
holder.item.divider.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ItemHorizontalDownloadBinding item;
|
||||
|
||||
@@ -86,28 +274,72 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter<DownloadHori
|
||||
|
||||
this.item = item;
|
||||
|
||||
item.downloadedSongTitleTextView.setSelected(true);
|
||||
item.downloadedSongArtistTextView.setSelected(true);
|
||||
item.downloadedItemTitleTextView.setSelected(true);
|
||||
item.downloadedItemSubtitleTextView.setSelected(true);
|
||||
|
||||
itemView.setOnClickListener(v -> onClick());
|
||||
itemView.setOnLongClickListener(v -> onLongClick());
|
||||
|
||||
item.downloadedSongMoreButton.setOnClickListener(v -> onLongClick());
|
||||
item.downloadedItemMoreButton.setOnClickListener(v -> onLongClick());
|
||||
}
|
||||
|
||||
public void onClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(songs));
|
||||
bundle.putInt(Constants.ITEM_POSITION, getBindingAdapterPosition());
|
||||
|
||||
click.onMediaClick(bundle);
|
||||
switch (view) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(grouped));
|
||||
bundle.putInt(Constants.ITEM_POSITION, getBindingAdapterPosition());
|
||||
click.onMediaClick(bundle);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
bundle.putString(Constants.DOWNLOAD_TYPE_ALBUM, grouped.get(getBindingAdapterPosition()).getAlbumId());
|
||||
click.onAlbumClick(bundle);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
bundle.putString(Constants.DOWNLOAD_TYPE_ARTIST, grouped.get(getBindingAdapterPosition()).getArtistId());
|
||||
click.onArtistClick(bundle);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
bundle.putString(Constants.DOWNLOAD_TYPE_GENRE, grouped.get(getBindingAdapterPosition()).getGenre());
|
||||
click.onGenreClick(bundle);
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
bundle.putString(Constants.DOWNLOAD_TYPE_YEAR, grouped.get(getBindingAdapterPosition()).getYear().toString());
|
||||
click.onYearClick(bundle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onLongClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.TRACK_OBJECT, songs.get(getBindingAdapterPosition()));
|
||||
ArrayList<Child> filteredSongs = new ArrayList<>();
|
||||
|
||||
click.onMediaLongClick(bundle);
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
switch (view) {
|
||||
case Constants.DOWNLOAD_TYPE_TRACK:
|
||||
filteredSongs.add(grouped.get(getBindingAdapterPosition()));
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_ALBUM:
|
||||
filteredSongs.addAll(filterSong(Constants.DOWNLOAD_TYPE_ALBUM, grouped.get(getBindingAdapterPosition()).getAlbumId(), songs));
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_ARTIST:
|
||||
filteredSongs.addAll(filterSong(Constants.DOWNLOAD_TYPE_ARTIST, grouped.get(getBindingAdapterPosition()).getArtistId(), songs));
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_GENRE:
|
||||
filteredSongs.addAll(filterSong(Constants.DOWNLOAD_TYPE_GENRE, grouped.get(getBindingAdapterPosition()).getGenre(), songs));
|
||||
break;
|
||||
case Constants.DOWNLOAD_TYPE_YEAR:
|
||||
filteredSongs.addAll(filterSong(Constants.DOWNLOAD_TYPE_YEAR, grouped.get(getBindingAdapterPosition()).getYear().toString(), songs));
|
||||
break;
|
||||
}
|
||||
|
||||
if (filteredSongs.isEmpty()) return false;
|
||||
|
||||
bundle.putParcelableArrayList(Constants.DOWNLOAD_GROUP, new ArrayList<>(filteredSongs));
|
||||
bundle.putString(Constants.DOWNLOAD_GROUP_TITLE, item.downloadedItemTitleTextView.getText().toString());
|
||||
bundle.putString(Constants.DOWNLOAD_GROUP_SUBTITLE, item.downloadedItemSubtitleTextView.getText().toString());
|
||||
click.onDownloadGroupLongClick(bundle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class GridTrackAdapter extends RecyclerView.Adapter<GridTrackAdapter.View
|
||||
Chronology item = items.get(position);
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), item.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), item.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.trackCoverImageView);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class InternetRadioStationAdapter extends RecyclerView.Adapter<InternetRa
|
||||
holder.item.internetRadioStationSubtitleTextView.setText(internetRadioStation.getStreamUrl());
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), internetRadioStation.getStreamUrl())
|
||||
.from(holder.itemView.getContext(), internetRadioStation.getStreamUrl(), CustomGlideRequest.ResourceType.Radio)
|
||||
.build()
|
||||
.into(holder.item.internetRadioStationCoverImageView);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.adapter;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -42,10 +43,17 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
||||
|
||||
holder.item.musicDirectoryTitleTextView.setText(child.getTitle());
|
||||
|
||||
CustomGlideRequest.ResourceType type = child.isDir()
|
||||
? CustomGlideRequest.ResourceType.Directory
|
||||
: CustomGlideRequest.ResourceType.Song;
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), child.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), child.getCoverArtId(), type)
|
||||
.build()
|
||||
.into(holder.item.musicDirectoryCoverImageView);
|
||||
|
||||
holder.item.musicDirectoryMoreButton.setVisibility(child.isDir() ? View.VISIBLE : View.INVISIBLE);
|
||||
holder.item.musicDirectoryPlayButton.setVisibility(child.isDir() ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,6 +77,8 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
||||
item.musicDirectoryTitleTextView.setSelected(true);
|
||||
|
||||
itemView.setOnClickListener(v -> onClick());
|
||||
itemView.setOnLongClickListener(v -> onLongClick());
|
||||
|
||||
item.musicDirectoryMoreButton.setOnClickListener(v -> onClick());
|
||||
}
|
||||
|
||||
@@ -76,7 +86,7 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
if (children.get(getBindingAdapterPosition()).isDir()) {
|
||||
bundle.putParcelable(Constants.MUSIC_DIRECTORY_OBJECT, children.get(getBindingAdapterPosition()));
|
||||
bundle.putString(Constants.MUSIC_DIRECTORY_ID, children.get(getBindingAdapterPosition()).getId());
|
||||
click.onMusicDirectoryClick(bundle);
|
||||
} else {
|
||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(children));
|
||||
@@ -84,5 +94,18 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
||||
click.onMediaClick(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onLongClick() {
|
||||
if (!children.get(getBindingAdapterPosition()).isDir()) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.TRACK_OBJECT, children.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onMediaLongClick(bundle);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class MusicFolderAdapter extends RecyclerView.Adapter<MusicFolderAdapter.
|
||||
holder.item.musicFolderTitleTextView.setText(musicFolder.getName());
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), musicFolder.getName())
|
||||
.from(holder.itemView.getContext(), musicFolder.getName(), CustomGlideRequest.ResourceType.Folder)
|
||||
.build()
|
||||
.into(holder.item.musicFolderCoverImageView);
|
||||
}
|
||||
|
||||
@@ -10,15 +10,17 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cappielloantonio.tempo.databinding.ItemLibraryMusicIndexBinding;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.helper.recyclerview.FastScrollbar;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Artist;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@UnstableApi
|
||||
public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.ViewHolder> {
|
||||
public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.ViewHolder> implements FastScrollbar.BubbleTextGetter {
|
||||
private final ClickCallback click;
|
||||
|
||||
private List<Artist> artists;
|
||||
@@ -42,7 +44,7 @@ public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.Vi
|
||||
holder.item.musicIndexTitleTextView.setText(artist.getName());
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), artist.getName())
|
||||
.from(holder.itemView.getContext(), artist.getName(), CustomGlideRequest.ResourceType.Directory)
|
||||
.build()
|
||||
.into(holder.item.musicIndexCoverImageView);
|
||||
}
|
||||
@@ -57,6 +59,11 @@ public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.Vi
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextToShowInBubble(int pos) {
|
||||
return artists != null && !artists.isEmpty() ? Character.toString(Objects.requireNonNull(artists.get(pos).getName().toUpperCase()).charAt(0)) : null;
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ItemLibraryMusicIndexBinding item;
|
||||
|
||||
@@ -73,7 +80,7 @@ public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.Vi
|
||||
|
||||
public void onClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.MUSIC_INDEX_OBJECT, artists.get(getBindingAdapterPosition()));
|
||||
bundle.putString(Constants.MUSIC_DIRECTORY_ID, artists.get(getBindingAdapterPosition()).getId());
|
||||
click.onMusicIndexClick(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueue
|
||||
holder.item.queueSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.queueSongCoverImageView);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class PlaylistDialogSongHorizontalAdapter extends RecyclerView.Adapter<Pl
|
||||
holder.item.playlistDialogSongDurationTextView.setText(MusicUtil.getReadableDurationString(song.getDuration(), false));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.playlistDialogSongCoverImageView);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class PlaylistHorizontalAdapter extends RecyclerView.Adapter<PlaylistHori
|
||||
holder.item.playlistSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.playlist_counted_tracks, playlist.getSongCount(), MusicUtil.getReadableDurationString(playlist.getDuration(), false)));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), playlist.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), playlist.getCoverArtId(), CustomGlideRequest.ResourceType.Playlist)
|
||||
.build()
|
||||
.into(holder.item.playlistCoverImageView);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class PodcastChannelCatalogueAdapter extends RecyclerView.Adapter<Podcast
|
||||
holder.item.podcastChannelTitleLabel.setText(MusicUtil.getReadableString(podcastChannel.getTitle()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), podcastChannel.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), podcastChannel.getCoverArtId(), CustomGlideRequest.ResourceType.Podcast)
|
||||
.build()
|
||||
.into(holder.item.podcastChannelCatalogueCoverImageView);
|
||||
}
|
||||
@@ -128,14 +128,14 @@ public class PodcastChannelCatalogueAdapter extends RecyclerView.Adapter<Podcast
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.PODCAST_CHANNEL_OBJECT, podcastChannels.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onAlbumClick(bundle);
|
||||
click.onPodcastChannelClick(bundle);
|
||||
}
|
||||
|
||||
private boolean onLongClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.PODCAST_CHANNEL_OBJECT, podcastChannels.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onAlbumLongClick(bundle);
|
||||
click.onPodcastChannelLongClick(bundle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class PodcastChannelHorizontalAdapter extends RecyclerView.Adapter<Podcas
|
||||
holder.item.podcastChannelDescriptionTextView.setText(MusicUtil.getReadableString(podcastChannel.getDescription()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), podcastChannel.getOriginalImageUrl())
|
||||
.from(holder.itemView.getContext(), podcastChannel.getCoverArtId(), CustomGlideRequest.ResourceType.Podcast)
|
||||
.build()
|
||||
.into(holder.item.podcastChannelCoverImageView);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.adapter;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -18,11 +19,14 @@ import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAdapter.ViewHolder> {
|
||||
private final ClickCallback click;
|
||||
|
||||
private List<PodcastEpisode> podcastEpisodes;
|
||||
private List<PodcastEpisode> podcastEpisodesFull;
|
||||
|
||||
public PodcastEpisodeAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
@@ -47,9 +51,13 @@ public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAd
|
||||
holder.item.podcastDescriptionText.setText(MusicUtil.getReadableString(podcastEpisode.getDescription()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), podcastEpisode.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), podcastEpisode.getCoverArtId(), CustomGlideRequest.ResourceType.Podcast)
|
||||
.build()
|
||||
.into(holder.item.podcastCoverImageView);
|
||||
|
||||
holder.item.podcastPlayButton.setEnabled(podcastEpisode.getStatus().equals("completed"));
|
||||
holder.item.podcastMoreButton.setVisibility(podcastEpisode.getStatus().equals("completed") ? View.VISIBLE : View.GONE);
|
||||
holder.item.podcastDownloadRequestButton.setVisibility(podcastEpisode.getStatus().equals("completed") ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,7 +66,8 @@ public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAd
|
||||
}
|
||||
|
||||
public void setItems(List<PodcastEpisode> podcastEpisodes) {
|
||||
this.podcastEpisodes = podcastEpisodes;
|
||||
this.podcastEpisodesFull = podcastEpisodes;
|
||||
this.podcastEpisodes = podcastEpisodesFull.stream().filter(podcastEpisode -> Objects.equals(podcastEpisode.getStatus(), "completed")).collect(Collectors.toList());
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@@ -85,22 +94,57 @@ public class PodcastEpisodeAdapter extends RecyclerView.Adapter<PodcastEpisodeAd
|
||||
|
||||
item.podcastPlayButton.setOnClickListener(v -> onClick());
|
||||
item.podcastMoreButton.setOnClickListener(v -> openMore());
|
||||
item.podcastDownloadRequestButton.setOnClickListener(v -> requestDownload());
|
||||
}
|
||||
|
||||
public void onClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||
PodcastEpisode podcastEpisode = podcastEpisodes.get(getBindingAdapterPosition());
|
||||
|
||||
click.onPodcastEpisodeClick(bundle);
|
||||
if (podcastEpisode.getStatus().equals("completed")) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onPodcastEpisodeClick(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean openMore() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||
PodcastEpisode podcastEpisode = podcastEpisodes.get(getBindingAdapterPosition());
|
||||
|
||||
click.onPodcastEpisodeLongClick(bundle);
|
||||
if (podcastEpisode.getStatus().equals("completed")) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||
|
||||
return true;
|
||||
click.onPodcastEpisodeLongClick(bundle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void requestDownload() {
|
||||
PodcastEpisode podcastEpisode = podcastEpisodes.get(getBindingAdapterPosition());
|
||||
|
||||
if (!podcastEpisode.getStatus().equals("completed")) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.PODCAST_OBJECT, podcastEpisodes.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onPodcastEpisodeAltClick(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sort(String order) {
|
||||
switch (order) {
|
||||
case Constants.PODCAST_FILTER_BY_DOWNLOAD:
|
||||
podcastEpisodes = podcastEpisodesFull.stream().filter(podcastEpisode -> Objects.equals(podcastEpisode.getStatus(), "completed")).collect(Collectors.toList());
|
||||
break;
|
||||
case Constants.PODCAST_FILTER_BY_ALL:
|
||||
podcastEpisodes = podcastEpisodesFull;
|
||||
break;
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.cappielloantonio.tempo.ui.adapter;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.ItemHorizontalShareBinding;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ShareHorizontalAdapter extends RecyclerView.Adapter<ShareHorizontalAdapter.ViewHolder> {
|
||||
private final ClickCallback click;
|
||||
|
||||
private List<Share> shares;
|
||||
|
||||
public ShareHorizontalAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.shares = Collections.emptyList();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemHorizontalShareBinding view = ItemHorizontalShareBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
Share share = shares.get(position);
|
||||
|
||||
holder.item.shareTitleTextView.setText(MusicUtil.getReadableString(share.getDescription()));
|
||||
holder.item.shareSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.share_subtitle_item, UIUtil.getReadableDate(share.getExpires())));
|
||||
|
||||
if (share.getEntries() != null && !share.getEntries().isEmpty()) CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), share.getEntries().get(0).getCoverArtId(), CustomGlideRequest.ResourceType.Album)
|
||||
.build()
|
||||
.into(holder.item.shareCoverImageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return shares.size();
|
||||
}
|
||||
|
||||
public void setItems(List<Share> shares) {
|
||||
this.shares = shares;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public Share getItem(int id) {
|
||||
return shares.get(id);
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ItemHorizontalShareBinding item;
|
||||
|
||||
ViewHolder(ItemHorizontalShareBinding item) {
|
||||
super(item.getRoot());
|
||||
|
||||
this.item = item;
|
||||
|
||||
item.shareTitleTextView.setSelected(true);
|
||||
item.shareSubtitleTextView.setSelected(true);
|
||||
|
||||
itemView.setOnClickListener(v -> onClick());
|
||||
itemView.setOnLongClickListener(v -> onLongClick());
|
||||
|
||||
item.shareButton.setOnClickListener(v -> onLongClick());
|
||||
}
|
||||
|
||||
private void onClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.SHARE_OBJECT, shares.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onShareClick(bundle);
|
||||
}
|
||||
|
||||
private boolean onLongClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.SHARE_OBJECT, shares.get(getBindingAdapterPosition()));
|
||||
|
||||
click.onShareLongClick(bundle);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class SimilarTrackAdapter extends RecyclerView.Adapter<SimilarTrackAdapte
|
||||
holder.item.titleTrackLabel.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.trackCoverImageView);
|
||||
}
|
||||
|
||||
@@ -25,13 +25,15 @@ import java.util.List;
|
||||
@UnstableApi
|
||||
public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAdapter.ViewHolder> {
|
||||
private final ClickCallback click;
|
||||
private final boolean isCoverVisible;
|
||||
private final boolean showCoverArt;
|
||||
private final boolean showAlbum;
|
||||
|
||||
private List<Child> songs;
|
||||
|
||||
public SongHorizontalAdapter(ClickCallback click, boolean isCoverVisible) {
|
||||
public SongHorizontalAdapter(ClickCallback click, boolean showCoverArt, boolean showAlbum) {
|
||||
this.click = click;
|
||||
this.isCoverVisible = isCoverVisible;
|
||||
this.showCoverArt = showCoverArt;
|
||||
this.showAlbum = showAlbum;
|
||||
this.songs = Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -47,8 +49,8 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
Child song = songs.get(position);
|
||||
|
||||
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration() != null ? song.getDuration() : 0, false)));
|
||||
holder.item.trackNumberTextView.setText(String.valueOf(song.getTrack()));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(this.showAlbum ? song.getAlbum() : song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration(), false)));
|
||||
holder.item.trackNumberTextView.setText(MusicUtil.getReadableTrackNumber(holder.itemView.getContext(), song.getTrack()));
|
||||
|
||||
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
|
||||
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.VISIBLE);
|
||||
@@ -56,16 +58,15 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (isCoverVisible) CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||
if (showCoverArt) CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId(), CustomGlideRequest.ResourceType.Song)
|
||||
.build()
|
||||
.into(holder.item.songCoverImageView);
|
||||
|
||||
if (isCoverVisible) holder.item.trackNumberTextView.setVisibility(View.INVISIBLE);
|
||||
holder.item.trackNumberTextView.setVisibility(showCoverArt ? View.INVISIBLE : View.VISIBLE);
|
||||
holder.item.songCoverImageView.setVisibility(showCoverArt ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
if (!isCoverVisible) holder.item.songCoverImageView.setVisibility(View.INVISIBLE);
|
||||
|
||||
if (!isCoverVisible && (position > 0 && songs.get(position - 1) != null && songs.get(position - 1).getDiscNumber() != null && songs.get(position).getDiscNumber() != null && songs.get(position - 1).getDiscNumber() < songs.get(position).getDiscNumber())) {
|
||||
if (!showCoverArt && (position > 0 && songs.get(position - 1) != null && songs.get(position - 1).getDiscNumber() != null && songs.get(position).getDiscNumber() != null && songs.get(position - 1).getDiscNumber() < songs.get(position).getDiscNumber())) {
|
||||
holder.item.differentDiskDivider.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -76,10 +77,20 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
}
|
||||
|
||||
public void setItems(List<Child> songs) {
|
||||
this.songs = songs;
|
||||
this.songs = songs != null ? songs : Collections.emptyList();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public Child getItem(int id) {
|
||||
return songs.get(id);
|
||||
}
|
||||
@@ -104,7 +115,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
public void onClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(MusicUtil.limitPlayableMedia(songs, getBindingAdapterPosition())));
|
||||
bundle.putInt(Constants.ITEM_POSITION, MusicUtil.getPlayableMediaPosition(getBindingAdapterPosition()));
|
||||
bundle.putInt(Constants.ITEM_POSITION, MusicUtil.getPlayableMediaPosition(songs, getBindingAdapterPosition()));
|
||||
|
||||
click.onMediaClick(bundle);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogBatteryOptimizationBinding;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class BatteryOptimizationDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
DialogBatteryOptimizationBinding bind = DialogBatteryOptimizationBinding.inflate(getLayoutInflater());
|
||||
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.activity_battery_optimizations_title)
|
||||
.setPositiveButton(R.string.battery_optimization_positive_button, (dialog, listener) -> openPowerSettings())
|
||||
.setNeutralButton(R.string.battery_optimization_neutral_button, (dialog, listener) -> Preferences.dontAskForOptimization())
|
||||
.setNegativeButton(R.string.battery_optimization_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
private void openPowerSettings() {
|
||||
Intent intent = new Intent();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -10,22 +9,18 @@ import androidx.fragment.app.DialogFragment;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogConnectionAlertBinding;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConnectionAlertDialog extends DialogFragment {
|
||||
private static final String TAG = "ServerUnreachableDialog";
|
||||
|
||||
private DialogConnectionAlertBinding bind;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogConnectionAlertBinding.inflate(getLayoutInflater());
|
||||
DialogConnectionAlertBinding bind = DialogConnectionAlertBinding.inflate(getLayoutInflater());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.connection_alert_dialog_title)
|
||||
.setPositiveButton(R.string.connection_alert_dialog_positive_button, (dialog, id) -> dialog.cancel())
|
||||
.setNegativeButton(R.string.connection_alert_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
@@ -45,16 +40,12 @@ public class ConnectionAlertDialog extends DialogFragment {
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
bind = null;
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
Preferences.setDataSavingMode(true);
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogDeleteDownloadStorageBinding;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class DeleteDownloadStorageDialog extends DialogFragment {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
DialogDeleteDownloadStorageBinding bind = DialogDeleteDownloadStorageBinding.inflate(getLayoutInflater());
|
||||
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.delete_download_storage_dialog_title)
|
||||
.setPositiveButton(R.string.delete_download_storage_dialog_positive_button, null)
|
||||
.setNegativeButton(R.string.delete_download_storage_dialog_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(v -> {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||
negativeButton.setOnClickListener(v -> {
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogDownloadDirectoryBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class DownloadDirectoryDialog extends DialogFragment {
|
||||
private final DialogClickCallback dialogClickCallback;
|
||||
|
||||
public DownloadDirectoryDialog(DialogClickCallback dialogClickCallback) {
|
||||
this.dialogClickCallback = dialogClickCallback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
DialogDownloadDirectoryBinding bind = DialogDownloadDirectoryBinding.inflate(getLayoutInflater());
|
||||
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.download_directory_dialog_title)
|
||||
.setPositiveButton(R.string.download_directory_dialog_positive_button, null)
|
||||
.setNegativeButton(R.string.download_directory_dialog_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(v -> {
|
||||
dialogClickCallback.onPositiveClick();
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||
negativeButton.setOnClickListener(v -> {
|
||||
dialogClickCallback.onNegativeClick();
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogDownloadStorageBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class DownloadStorageDialog extends DialogFragment {
|
||||
private final DialogClickCallback dialogClickCallback;
|
||||
|
||||
public DownloadStorageDialog(DialogClickCallback dialogClickCallback) {
|
||||
this.dialogClickCallback = dialogClickCallback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
DialogDownloadStorageBinding bind = DialogDownloadStorageBinding.inflate(getLayoutInflater());
|
||||
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.download_storage_dialog_title)
|
||||
.setPositiveButton(R.string.download_storage_external_dialog_positive_button, null)
|
||||
.setNegativeButton(R.string.download_storage_internal_dialog_negative_button, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(v -> {
|
||||
int currentPreference = Preferences.getDownloadStoragePreference();
|
||||
int newPreference = 1;
|
||||
|
||||
if (currentPreference != newPreference) {
|
||||
Preferences.setDownloadStoragePreference(newPreference);
|
||||
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||
dialogClickCallback.onPositiveClick();
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||
negativeButton.setOnClickListener(v -> {
|
||||
int currentPreference = Preferences.getDownloadStoragePreference();
|
||||
int newPreference = 0;
|
||||
|
||||
if (currentPreference != newPreference) {
|
||||
Preferences.setDownloadStoragePreference(newPreference);
|
||||
DownloadUtil.getDownloadTracker(requireContext()).removeAll();
|
||||
dialogClickCallback.onNegativeClick();
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
@@ -17,6 +16,7 @@ import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaylistChooserViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -30,17 +30,15 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogPlaylistChooserBinding.inflate(getLayoutInflater());
|
||||
|
||||
playlistChooserViewModel = new ViewModelProvider(requireActivity()).get(PlaylistChooserViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.playlist_chooser_dialog_title)
|
||||
.setNeutralButton(R.string.playlist_chooser_dialog_neutral_button, (dialog, id) -> {
|
||||
})
|
||||
.setNegativeButton(R.string.playlist_chooser_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNeutralButton(R.string.playlist_chooser_dialog_neutral_button, (dialog, id) -> { })
|
||||
.setNegativeButton(R.string.playlist_chooser_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,11 +61,12 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.TRACK_OBJECT, playlistChooserViewModel.getSongToAdd());
|
||||
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog(null);
|
||||
dialog.setArguments(bundle);
|
||||
dialog.show(requireActivity().getSupportFragmentManager(), null);
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
@@ -14,10 +17,13 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogPlaylistEditorBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
|
||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogSongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaylistEditorViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
@@ -26,25 +32,29 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
private DialogPlaylistEditorBinding bind;
|
||||
private PlaylistEditorViewModel playlistEditorViewModel;
|
||||
|
||||
private final PlaylistCallback playlistCallback;
|
||||
|
||||
private String playlistName;
|
||||
private PlaylistDialogSongHorizontalAdapter playlistDialogSongHorizontalAdapter;
|
||||
|
||||
public PlaylistEditorDialog(PlaylistCallback playlistCallback) {
|
||||
this.playlistCallback = playlistCallback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogPlaylistEditorBinding.inflate(getLayoutInflater());
|
||||
|
||||
playlistEditorViewModel = new ViewModelProvider(requireActivity()).get(PlaylistEditorViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.playlist_editor_dialog_title)
|
||||
.setPositiveButton(R.string.playlist_editor_dialog_positive_button, (dialog, id) -> {
|
||||
})
|
||||
.setPositiveButton(R.string.playlist_editor_dialog_positive_button, (dialog, id) -> { })
|
||||
.setNeutralButton(R.string.playlist_editor_dialog_neutral_button, (dialog, id) -> dialog.cancel())
|
||||
.setNegativeButton(R.string.playlist_editor_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNegativeButton(R.string.playlist_editor_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,7 +87,9 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
androidx.appcompat.app.AlertDialog alertDialog = (androidx.appcompat.app.AlertDialog) Objects.requireNonNull(getDialog());
|
||||
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
if (playlistEditorViewModel.getSongToAdd() != null) {
|
||||
playlistEditorViewModel.createPlaylist(playlistName);
|
||||
@@ -85,14 +97,24 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
playlistEditorViewModel.updatePlaylist(playlistName);
|
||||
}
|
||||
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
dialogDismiss();
|
||||
}
|
||||
});
|
||||
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
alertDialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
playlistEditorViewModel.deletePlaylist();
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
dialogDismiss();
|
||||
});
|
||||
|
||||
bind.playlistShareButton.setOnClickListener(view -> {
|
||||
playlistEditorViewModel.sharePlaylist().observe(requireActivity(), sharedPlaylist -> {
|
||||
ClipboardManager clipboardManager = (ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clipData = ClipData.newPlainText(getString(R.string.app_name), sharedPlaylist.getUrl());
|
||||
clipboardManager.setPrimaryClip(clipData);
|
||||
});
|
||||
});
|
||||
|
||||
bind.playlistShareButton.setVisibility(Preferences.isSharingEnabled() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void initSongsView() {
|
||||
@@ -102,7 +124,9 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
playlistDialogSongHorizontalAdapter = new PlaylistDialogSongHorizontalAdapter();
|
||||
bind.playlistSongRecyclerView.setAdapter(playlistDialogSongHorizontalAdapter);
|
||||
|
||||
playlistEditorViewModel.getPlaylistSongLiveList().observe(requireActivity(), songs -> playlistDialogSongHorizontalAdapter.setItems(songs));
|
||||
playlistEditorViewModel.getPlaylistSongLiveList().observe(requireActivity(), songs -> {
|
||||
if (songs != null) playlistDialogSongHorizontalAdapter.setItems(songs);
|
||||
});
|
||||
|
||||
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
|
||||
int originalPosition = -1;
|
||||
@@ -157,4 +181,11 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void dialogDismiss() {
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
if (playlistCallback != null) {
|
||||
playlistCallback.onDismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
@@ -13,13 +13,15 @@ import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogPodcastChannelEditorBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.PodcastCallback;
|
||||
import com.cappielloantonio.tempo.viewmodel.PodcastChannelEditorViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class PodcastChannelEditorDialog extends DialogFragment {
|
||||
private DialogPodcastChannelEditorBinding bind;
|
||||
private PodcastChannelEditorViewModel podcastChannelEditorViewModel;
|
||||
private PodcastCallback podcastCallback;
|
||||
|
||||
private final PodcastCallback podcastCallback;
|
||||
|
||||
private String channelUrl;
|
||||
|
||||
@@ -31,17 +33,15 @@ public class PodcastChannelEditorDialog extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogPodcastChannelEditorBinding.inflate(getLayoutInflater());
|
||||
|
||||
podcastChannelEditorViewModel = new ViewModelProvider(requireActivity()).get(PodcastChannelEditorViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(getActivity())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.podcast_channel_editor_dialog_title)
|
||||
.setPositiveButton(R.string.radio_editor_dialog_positive_button, (dialog, id) -> {
|
||||
})
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setPositiveButton(R.string.radio_editor_dialog_positive_button, (dialog, id) -> { })
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> dialog.cancel())
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,14 +58,19 @@ public class PodcastChannelEditorDialog extends DialogFragment {
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
podcastChannelEditorViewModel.createChannel(channelUrl);
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||
if (dialog != null) {
|
||||
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
podcastChannelEditorViewModel.createChannel(channelUrl);
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean validateInput() {
|
||||
channelUrl = Objects.requireNonNull(bind.podcastChannelRssUrlNameTextView.getText()).toString().trim();
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.cappielloantonio.tempo.ui.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
@@ -15,13 +14,15 @@ import com.cappielloantonio.tempo.interfaces.RadioCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.RadioEditorViewModel;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class RadioEditorDialog extends DialogFragment {
|
||||
private DialogRadioEditorBinding bind;
|
||||
private RadioEditorViewModel radioEditorViewModel;
|
||||
private RadioCallback radioCallback;
|
||||
|
||||
private final RadioCallback radioCallback;
|
||||
|
||||
private String radioName;
|
||||
private String radioStreamURL;
|
||||
@@ -35,26 +36,36 @@ public class RadioEditorDialog extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
bind = DialogRadioEditorBinding.inflate(getLayoutInflater());
|
||||
|
||||
radioEditorViewModel = new ViewModelProvider(requireActivity()).get(RadioEditorViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
|
||||
builder.setView(bind.getRoot())
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(bind.getRoot())
|
||||
.setTitle(R.string.radio_editor_dialog_title)
|
||||
.setPositiveButton(R.string.radio_editor_dialog_positive_button, (dialog, id) -> {
|
||||
if (validateInput()) {
|
||||
if (radioEditorViewModel.getRadioToEdit() == null) {
|
||||
radioEditorViewModel.createRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
} else {
|
||||
radioEditorViewModel.updateRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
}
|
||||
dismissDialog();
|
||||
}
|
||||
})
|
||||
.setNeutralButton(R.string.radio_editor_dialog_neutral_button, (dialog, id) -> dialog.cancel())
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> dialog.cancel());
|
||||
|
||||
return builder.create();
|
||||
.setNeutralButton(R.string.radio_editor_dialog_neutral_button, (dialog, id) -> {
|
||||
radioEditorViewModel.deleteRadio();
|
||||
dismissDialog();
|
||||
})
|
||||
.setNegativeButton(R.string.radio_editor_dialog_negative_button, (dialog, id) -> {
|
||||
dialog.cancel();
|
||||
})
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
setParameterInfo();
|
||||
setButtonAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,25 +86,6 @@ public class RadioEditorDialog extends DialogFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void setButtonAction() {
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
if (validateInput()) {
|
||||
if (radioEditorViewModel.getRadioToEdit() == null) {
|
||||
radioEditorViewModel.createRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
} else {
|
||||
radioEditorViewModel.updateRadio(radioName, radioStreamURL, radioHomepageURL.isEmpty() ? null : radioHomepageURL);
|
||||
}
|
||||
|
||||
dismissDialog();
|
||||
}
|
||||
});
|
||||
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
radioEditorViewModel.deleteRadio();
|
||||
dismissDialog();
|
||||
});
|
||||
}
|
||||
|
||||
private boolean validateInput() {
|
||||
radioName = Objects.requireNonNull(bind.internetRadioStationNameTextView.getText()).toString().trim();
|
||||
radioStreamURL = Objects.requireNonNull(bind.internetRadioStationStreamUrlTextView.getText()).toString().trim();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user