Compare commits
465 Commits
Author | SHA1 | Date |
---|---|---|
Óscar García Amor | 90205fe0fb | |
Óscar García Amor | 2165ce75b3 | |
Óscar García Amor | 8b3ee0a8d6 | |
birdbird | 695b2df63f | |
tzugen | 798d795e81 | |
tzugen | ecfce59e0f | |
tzugen | de0cb7713b | |
tzugen | 78bfab3753 | |
tzugen | b955d77152 | |
tzugen | b11694d6a2 | |
tzugen | 31a1fdace1 | |
tzugen | 5b03b632fd | |
tzugen | 152b1d261a | |
tzugen | 53a1a5545a | |
tzugen | ad54db5bcb | |
tzugen | 177329abcf | |
tzugen | 241e51015f | |
tzugen | 60dbe70ca5 | |
tzugen | 8490f7115d | |
tzugen | ee67f4c744 | |
tzugen | 3a3bd10fdb | |
birdbird | 3445576dc9 | |
tzugen | 8c40f662a1 | |
birdbird | 6c6227ce41 | |
tzugen | 240a2fa8f6 | |
tzugen | 7de775dc26 | |
birdbird | d034fc9c71 | |
birdbird | 05ada9297d | |
Maxence G | aa6c037b20 | |
Maxence G | b8c924be27 | |
Maxence G | 0929a6a1bd | |
Maxence G | fefee74a66 | |
Maxence G | 37e3ce09c1 | |
Maxence G | 16b3fcad32 | |
Maxence G | d6aebd9989 | |
Maxence G | 3f408600cb | |
Maxence G | 9014b47b74 | |
tzugen | ac489ae8b9 | |
tzugen | e7f8fa21cb | |
tzugen | b1c3cabfef | |
tzugen | 77865a143d | |
Óscar García Amor | ff9c7b2435 | |
Óscar García Amor | 737563bf6b | |
tzugen | 9a73d72fa4 | |
tzugen | 98ce519014 | |
tzugen | 83fc54d332 | |
Maxence G | a2b9c6b9a3 | |
Maxence G | 5ae56d26c5 | |
Maxence G | 4efb6dcb58 | |
tzugen | 8a90e98989 | |
tzugen | 46a8f4640d | |
tzugen | ab41966943 | |
tzugen | 00d7ce326c | |
Maxence G | bc4b0aa832 | |
Maxence G | 23fd336ffd | |
Maxence G | b57a973510 | |
Maxence G | 8796006ced | |
Maxence G | 545b65921e | |
Maxence G | cf367ead92 | |
Maxence G | 9961213f09 | |
tzugen | 5deb7d4d58 | |
tzugen | 5f31eaaffe | |
tzugen | cad6477cd9 | |
tzugen | b440821ea8 | |
Holger Müller | 8663b9d50e | |
Óscar García Amor | 2bae243be0 | |
Óscar García Amor | 139e810186 | |
tzugen | 66443ba018 | |
tzugen | f8b78a47d2 | |
tzugen | 4cda114f4c | |
tzugen | d8b5b774ee | |
tzugen | b6730f5a93 | |
tzugen | 87c160610f | |
tzugen | 70f8b75019 | |
tzugen | 147d7cd46e | |
tzugen | 59e37e62a6 | |
tzugen | 1e571e165c | |
tzugen | 5e0dd14c4f | |
tzugen | 53ae0cd232 | |
tzugen | 608f86ac5f | |
tzugen | 669b51c0d2 | |
tzugen | 6e1478d896 | |
Nite | d9e4b8b3d3 | |
Nite | f790e29add | |
Nite | faf07f2887 | |
i-do-cpp | 057644f592 | |
tzugen | 926081f84c | |
tzugen | 4a00494647 | |
Nite | 34e0178db3 | |
Nite | cbe3992b01 | |
Nite | 46846bd5c9 | |
tzugen | 707339b88b | |
tzugen | 827654c0c1 | |
tzugen | 1d236aa6e3 | |
tzugen | 9cdba9a27a | |
tzugen | 7ba599f58c | |
tzugen | 2e1e627b7a | |
tzugen | d550eabf88 | |
tzugen | dda86b42c7 | |
tzugen | b6e890b26c | |
tzugen | c2ac1d436f | |
tzugen | 2aaa3c2119 | |
tzugen | 5d4aff1f21 | |
tzugen | 6115ac995f | |
tzugen | 647435fe55 | |
tzugen | 81d24f6cbb | |
tzugen | 69c78f4c37 | |
tzugen | 3691428a68 | |
tzugen | 788538ee6a | |
tzugen | 762aeec5d3 | |
tzugen | a3a0c7f41d | |
tzugen | 1564379bd1 | |
tzugen | 7d33770fd6 | |
tzugen | 728afad00c | |
Óscar García Amor | f121e297df | |
Óscar García Amor | 3f2cfb131a | |
Óscar García Amor | b8b4b81726 | |
Óscar García Amor | 383089a409 | |
Óscar García Amor | 8d8a5f05ea | |
Óscar García Amor | 46a2e5d67b | |
tzugen | 92ef78a36a | |
tzugen | e5021959c3 | |
tzugen | 3ca25ed1c6 | |
tzugen | 6da83db9df | |
tzugen | 9779844620 | |
tzugen | f936ad690c | |
tzugen | 5230ce011d | |
Holger Müller | a98c9e2ffd | |
tzugen | 0128a8b29d | |
tzugen | 41f5520f1f | |
tzugen | fd34199c27 | |
tzugen | bb77216eff | |
tzugen | e1f4ee15d5 | |
tzugen | d0959ffcb5 | |
tzugen | 4c22c8b41b | |
tzugen | ba1a1c5538 | |
tzugen | 7742f67796 | |
tzugen | 1a69507e34 | |
tzugen | 46fb7664c3 | |
tzugen | dd65a12b53 | |
tzugen | 2f7f47783a | |
tzugen | b1c2d020b5 | |
tzugen | 5dc9fda7a4 | |
tzugen | 1313fb6c0c | |
tzugen | 5966dd7299 | |
tzugen | 1703f02aad | |
tzugen | 922022ab03 | |
tzugen | bfc11f9924 | |
tzugen | e77b5abd3e | |
tzugen | 988bf62acf | |
tzugen | 1a46f7e2c6 | |
tzugen | 1d88c585c4 | |
tzugen | 287169649a | |
tzugen | 020f67d5e6 | |
Óscar García Amor | fcc57ae316 | |
tzugen | 0fd17bfe8c | |
tzugen | 0c016bff41 | |
tzugen | 12435ed9ec | |
tzugen | c2226ba202 | |
tzugen | 892b441c0d | |
tzugen | e53da92dac | |
tzugen | 2de59b2206 | |
tzugen | 34c13d7908 | |
tzugen | 88918bd839 | |
tzugen | f49063664b | |
tzugen | 126efd35c6 | |
tzugen | a6a052781d | |
tzugen | 107b01fd91 | |
tzugen | d05ac1489e | |
Holger Müller | fc94d28862 | |
Holger Müller | 8bec74e66a | |
Holger Müller | acf6c5a681 | |
tzugen | 81a21ce8b7 | |
Holger Müller | cf86101de2 | |
Nite | 5a44fcfe29 | |
Nite | 2aa5174fbd | |
Nite | cf7cef9831 | |
Nite | 423957d954 | |
Holger Müller | 0944bd2217 | |
Holger Müller | 7b750c692c | |
Holger Müller | c247e930c4 | |
tzugen | 44d68a71da | |
tzugen | 7a51c271ba | |
Óscar García Amor | 4d91068535 | |
tzugen | fe3b713241 | |
Holger Müller | 2f5704548c | |
tzugen | 17850980e1 | |
tzugen | 34d2b45d71 | |
tzugen | 555ef5b7ff | |
tzugen | c269243a0d | |
tzugen | 2d8b93301f | |
tzugen | e4a41de3ef | |
tzugen | f0447105d2 | |
tzugen | 65c4f2b100 | |
tzugen | f6f9683a9c | |
tzugen | c2d62e8688 | |
tzugen | e153565086 | |
tzugen | f30a582c7b | |
Holger Müller | cc5f29ca98 | |
Holger Müller | 8f18192c36 | |
Holger Müller | ae2055e324 | |
Nite | dee4675715 | |
Nite | e8c31db90f | |
Nite | 34fb63c783 | |
Nite | 9ee03aae2f | |
Nite | ebfc06c423 | |
Nite | 0587f4d837 | |
Nite | 6bfd06c6a0 | |
Óscar García Amor | 6442bae882 | |
Nite | c81c685800 | |
Holger Müller | 5941e5ab87 | |
Holger Müller | cf52d76698 | |
Cem Eren | 273ac8f9b8 | |
Nite | 465c211017 | |
Nite | 4fbedc3d2b | |
Cem Eren | 0961f56a7d | |
Cem Eren | eb0fa67431 | |
Óscar García Amor | 3d65c0a90c | |
Nite | 7dd479c0d2 | |
Nite | b5bfd87fcc | |
Philippe Daouadi | d03b633eeb | |
Nite | 4ee4b70e09 | |
Nite | 8675f25668 | |
Nite | 00dc87d5df | |
Nite | b7d1e4acf6 | |
Óscar García Amor | baa0c92c7a | |
Nite | 7c9d51f758 | |
Nite | 9bf7e99abd | |
Nite | b5e606455e | |
Óscar García Amor | e2ddb35ce3 | |
Óscar García Amor | 9581a17bd9 | |
Óscar García Amor | 3fbf47dedf | |
Óscar García Amor | 87aea4847c | |
Nite | f210a6e363 | |
Nite | a9ee09bc2f | |
Nite | bf96f36cb4 | |
Nite | da69fb9f1f | |
Nite | 5962cc2add | |
Nite | 0d4b400105 | |
Nite | ca9ba68a5e | |
Nite | d00a30940e | |
Nite | 586bc51a9c | |
Óscar García Amor | a53d5378bf | |
Óscar García Amor | 4778d18fb9 | |
Óscar García Amor | 2ff944d77d | |
Nite | 462719ccd1 | |
Nite | e39f232732 | |
dependabot[bot] | ca89d4b55e | |
Óscar García Amor | 3558479278 | |
dependabot[bot] | c66ea1c437 | |
Óscar García Amor | 23d0783164 | |
Óscar García Amor | 22c9db59c3 | |
Óscar García Amor | 121bcde18a | |
Óscar García Amor | 41a462708d | |
Nite | 5e24c3ab40 | |
tzugen | f68b46678a | |
tzugen | fd239e8e72 | |
tzugen | 10892f7527 | |
Óscar García Amor | e2a6a32b76 | |
Óscar García Amor | 0dd666415d | |
Óscar García Amor | 8f43f69a66 | |
Óscar García Amor | 193fe5ac3a | |
Óscar García Amor | 3588bb9380 | |
Óscar García Amor | 95b78a3c11 | |
tzugen | 9f5bc85cf2 | |
tzugen | 5a02467ee8 | |
tzugen | 4a996f8edc | |
tzugen | 8d5675f461 | |
tzugen | a5e8daa912 | |
tzugen | c2b23c4001 | |
tzugen | 10b83805a9 | |
Óscar García Amor | fbf5a63396 | |
Óscar García Amor | bb370bfc44 | |
Óscar García Amor | 2153650d8b | |
tzugen | 987fbbe02a | |
tzugen | b958dcabe4 | |
tzugen | d66de5955b | |
tzugen | 1eca5a756e | |
tzugen | dd92d00be5 | |
tzugen | 7531a4d4aa | |
tzugen | 82f45bd5dd | |
tzugen | c0ef964a3e | |
Nite | 34e232a43a | |
tzugen | 65347a20fa | |
tzugen | 7f42ed6a37 | |
dependabot[bot] | 95f37ba2e2 | |
tzugen | fa434342d9 | |
Nite | 17e49ff49e | |
Nite | adf72d6460 | |
Nite | 505405e87f | |
Nite | a3ad17692b | |
Nite | 5c3e2f6e37 | |
Nite | 941fbd907f | |
Nite | 095cf4ef4a | |
tzugen | 3be480a9a2 | |
tzugen | 5bfa0044ab | |
tzugen | 6b1fc7575a | |
tzugen | 6fcea86097 | |
tzugen | 28d5e5043f | |
tzugen | eb2aeabd5d | |
tzugen | a0da791b28 | |
tzugen | f4554ff29e | |
tzugen | 9acc5121a4 | |
Nite | 107146c8d9 | |
Nite | d51544f927 | |
Nite | 66e7732ec2 | |
Nite | fa4214a0ac | |
Nite | 34c5ced32e | |
Nite | 90638e5fd7 | |
tzugen | 6730713763 | |
dependabot[bot] | 38fa4964f8 | |
tzugen | b85230c056 | |
dependabot[bot] | fb85fb4e82 | |
Nite | 8c716da213 | |
tzugen | 80e587c1aa | |
tzugen | e337177715 | |
tzugen | d8cdc81424 | |
tzugen | 351ad914e7 | |
Nite | 0d24c87eef | |
tzugen | de04f4cbe6 | |
tzugen | 026aa79572 | |
tzugen | 6daa17efd5 | |
tzugen | f2948cd3db | |
tzugen | 2ac1ea3f89 | |
tzugen | f1e789ea9b | |
tzugen | bdac092eff | |
tzugen | aa33d7c882 | |
tzugen | 775f56c6fa | |
tzugen | 2f0ff384d0 | |
tzugen | 82d90a6aee | |
tzugen | b33fe2d451 | |
tzugen | 4e37a2483c | |
tzugen | 5dfb66eec2 | |
tzugen | ad793e27a5 | |
tzugen | 2086a6cac5 | |
tzugen | eeb2d13d96 | |
tzugen | f8a87f7c85 | |
tzugen | 7640f4c4aa | |
tzugen | d243ae1b44 | |
tzugen | 6277ee73c0 | |
tzugen | 7a2dbf65d9 | |
tzugen | 19d014709f | |
tzugen | d0e39efc50 | |
tzugen | e81b1ef8c2 | |
tzugen | 5f716f5008 | |
Nite | 5cf914f555 | |
Nite | 5c7cde2349 | |
Nite | 3f570636dd | |
Nite | 1d0bb944e1 | |
Nite | 5fac1b74a3 | |
Nite | d84a0a3929 | |
Nite | f3ac843a9c | |
tzugen | cddbe72752 | |
tzugen | 744282f10a | |
tzugen | 51d6a23208 | |
tzugen | a327a5b390 | |
tzugen | 36b581e3c1 | |
tzugen | 1ed9360bc7 | |
tzugen | ed152fa52a | |
Nite | 45e9728e0f | |
tzugen | b1cb70764c | |
tzugen | 00cd4fce44 | |
tzugen | 97eb753413 | |
tzugen | 00781ba7de | |
Nite | d6f908b80c | |
Nite | e019ec788d | |
tzugen | f73457298d | |
tzugen | c9e276dc76 | |
Nite | 28ef67a210 | |
tzugen | bb36116d70 | |
tzugen | 8830d76497 | |
tzugen | 61f23fa948 | |
tzugen | 92adcf47bd | |
tzugen | 7e3cb19bac | |
tzugen | dc312d4592 | |
dependabot[bot] | 435376b48b | |
Nite | 69825b28bb | |
Nite | 66df5b1daf | |
Nite | 38979bf9d4 | |
Nite | 68549992f4 | |
Nite | 2e482e02a2 | |
Nite | bf18c43d73 | |
Nite | 7fe4845305 | |
dependabot[bot] | c29b8ebe0e | |
Nite | 3bacabe480 | |
tzugen | aac73cd6d7 | |
tzugen | a66d07ae84 | |
Nite | 210ae35ee0 | |
Nite | 4e3102f131 | |
Nite | eba42b82dc | |
Nite | a34fc809d9 | |
Nite | ffb2d59886 | |
tzugen | a6e76e9d53 | |
tzugen | c4e2c786d1 | |
tzugen | 050161bbb0 | |
tzugen | f085a8ab65 | |
tzugen | dfb3561965 | |
tzugen | 8c99c84a90 | |
tzugen | 4fb4ab18da | |
tzugen | e6624ada9a | |
tzugen | a58e541ccc | |
tzugen | 416bc57eea | |
dependabot[bot] | 34c3936513 | |
dependabot[bot] | aece29559e | |
tzugen | e32b3461c9 | |
Nite | fec2d78d30 | |
Nite | 7ed91db250 | |
Nite | 7c43d01f8e | |
Nite | 5eaf9cccb1 | |
tzugen | bd23f54783 | |
tzugen | 5fe1921ca5 | |
tzugen | e5f7ca6310 | |
tzugen | 4d42c0d9d2 | |
tzugen | e19d43d6b3 | |
tzugen | 2d9a212b5c | |
Nite | f0c02f5551 | |
tzugen | 7c956566a0 | |
Nite | ada780ab25 | |
Nite | 70e42fb443 | |
tzugen | c83a9778fc | |
Nite | 6636d6a558 | |
Nite | 7ccb9d055c | |
Nite | 9782e18b6e | |
tzugen | eb2e6ada0a | |
Nite | cef1153f89 | |
tzugen | fea515a526 | |
tzugen | c44257f569 | |
tzugen | 939cd8583c | |
tzugen | 7d2923230c | |
tzugen | ece53f7687 | |
Nite | 2847a51674 | |
tzugen | 24092ce465 | |
tzugen | 6f676d20b0 | |
tzugen | 1d5b335f97 | |
tzugen | 9bc19ec044 | |
tzugen | 0bcf51a409 | |
Nite | f58c361e4e | |
Nite | 949e7e58ba | |
Nite | 77a2dcf935 | |
dependabot[bot] | 974099630a | |
Nite | 2197959507 | |
tzugen | 62bade916f | |
Nite | ccf39661ab | |
Nite | 7c66bc7ec8 | |
Nite | 477f6f5d7c | |
Nite | 784c65f96d | |
Nite | 16b2a99631 | |
Nite | 427034053c | |
Nite | 6905c68898 | |
Nite | 2b40577d4b | |
Nite | 23cca33d5a | |
tzugen | bc43cc6874 | |
tzugen | 83f4ecb15a | |
tzugen | fbdf6d846b | |
tzugen | d1e636f553 | |
tzugen | d7cd68c39e | |
tzugen | b892b7b8d3 | |
tzugen | 5c9b149bec | |
Nite | f752307a38 | |
Nite | 86f2aa1656 | |
Nite | 256f785d39 | |
Nite | 62150b77d3 | |
Nite | 652f85b070 | |
Nite | 381e29b2d1 | |
Nite | 4b4853374c |
|
@ -1,23 +1,33 @@
|
|||
version: 3
|
||||
version: 2.1
|
||||
parameters:
|
||||
memory-config:
|
||||
type: string
|
||||
default: "-Xmx3200m -Xms256m -XX:MaxMetaspaceSize=1g"
|
||||
memory-config-debug:
|
||||
type: string
|
||||
default: "-Xmx3200m -Xms256m -XX:MaxMetaspaceSize=1g -verbose:gc -Xlog:gc*"
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/android:api-29
|
||||
- image: cimg/android:2022.06.1
|
||||
working_directory: ~/ultrasonic
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
JVM_OPTS: << pipeline.parameters.memory-config >>
|
||||
JAVA_TOOL_OPTIONS: << pipeline.parameters.memory-config >>
|
||||
GRADLE_OPTS: << pipeline.parameters.memory-config >>
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-ultrasonic-{{ .Branch }}-{{ checksum "dependencies.gradle" }}
|
||||
- v1-ultrasonic-{{ .Branch }}
|
||||
- v1-ultrasonic
|
||||
- v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
|
||||
- v2-ultrasonic-{{ .Branch }}
|
||||
- v2-ultrasonic
|
||||
- run:
|
||||
name: configure gradle.properties for CI building
|
||||
command: |
|
||||
sed -i '/^org.gradle.jvmargs/d' gradle.properties
|
||||
sed -i 's/^org.gradle.daemon=true/org.gradle.daemon=false/g' gradle.properties
|
||||
cat gradle.properties
|
||||
- run:
|
||||
name: checkstyle
|
||||
command: ./gradlew -Pqc ktlintCheck
|
||||
|
@ -31,7 +41,6 @@ jobs:
|
|||
name: unit-tests
|
||||
command: |
|
||||
./gradlew ciTest testDebugUnitTest
|
||||
./gradlew jacocoFullReport
|
||||
- run:
|
||||
name: lint
|
||||
command: ./gradlew :ultrasonic:lintRelease
|
||||
|
@ -44,18 +53,16 @@ jobs:
|
|||
- save_cache:
|
||||
paths:
|
||||
- ~/.gradle
|
||||
key: v1-ultrasonic-{{ .Branch }}-{{ checksum "dependencies.gradle" }}
|
||||
key: v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
|
||||
- store_artifacts:
|
||||
path: ultrasonic/build/reports
|
||||
destination: reports
|
||||
- store_artifacts:
|
||||
path: subsonic-api/build/reports
|
||||
destination: reports
|
||||
- store_artifacts:
|
||||
path: build/reports/jacoco/jacocoFullReport/
|
||||
push_translations:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
- image: cimg/python:3.6
|
||||
working_directory: ~/ultrasonic
|
||||
steps:
|
||||
- checkout
|
||||
|
@ -75,15 +82,19 @@ jobs:
|
|||
tx push -s
|
||||
generate_signed_apk:
|
||||
docker:
|
||||
- image: circleci/android:api-28
|
||||
- image: cimg/android:2022.06.1
|
||||
working_directory: ~/ultrasonic
|
||||
environment:
|
||||
JVM_OPTS: << pipeline.parameters.memory-config >>
|
||||
JAVA_TOOL_OPTIONS: << pipeline.parameters.memory-config >>
|
||||
GRADLE_OPTS: << pipeline.parameters.memory-config >>
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-ultrasonic-{{ .Branch }}-{{ checksum "dependencies.gradle" }}
|
||||
- v1-ultrasonic-{{ .Branch }}
|
||||
- v1-ultrasonic
|
||||
- v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
|
||||
- v2-ultrasonic-{{ .Branch }}
|
||||
- v2-ultrasonic
|
||||
- run:
|
||||
name: decrypt ultrasonic-keystore
|
||||
command: openssl aes-256-cbc -K ${ULTRASONIC_KEYSTORE_KEY} -iv ${ULTRASONIC_KEYSTORE_IV} -in ultrasonic-keystore.enc -out ultrasonic-keystore -d
|
||||
|
@ -93,23 +104,24 @@ jobs:
|
|||
- run:
|
||||
name: sign release apk
|
||||
command: |
|
||||
export PATH="${JAVA_HOME}/bin:${PATH}"
|
||||
mkdir -p /tmp/ultrasonic-release
|
||||
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ~/ultrasonic/ultrasonic-keystore -storepass ${ULTRASONIC_KEYSTORE_STOREPASS} -keypass ${ULTRASONIC_KEYSTORE_KEYPASS} ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk ultrasonic
|
||||
jarsigner -verify ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk
|
||||
${ANDROID_HOME}/build-tools/27.0.0/zipalign -v 4 ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
|
||||
${ANDROID_HOME}/build-tools/32.0.0/zipalign -v 4 ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
|
||||
${ANDROID_HOME}/build-tools/32.0.0/apksigner sign --verbose --ks ~/ultrasonic/ultrasonic-keystore --ks-pass pass:${ULTRASONIC_KEYSTORE_STOREPASS} --key-pass pass:${ULTRASONIC_KEYSTORE_KEYPASS} /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
|
||||
${ANDROID_HOME}/build-tools/32.0.0/apksigner verify --verbose /tmp/ultrasonic-release/ultrasonic-${CIRCLE_TAG}.apk
|
||||
- persist_to_workspace:
|
||||
root: /tmp/ultrasonic-release
|
||||
paths:
|
||||
- ultrasonic-*.apk
|
||||
- ultrasonic-*.apk*
|
||||
publish_github_signed_apk:
|
||||
docker:
|
||||
- image: circleci/golang
|
||||
- image: cimg/go:1.18
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/ultrasonic-release
|
||||
- run:
|
||||
name: install ghr
|
||||
command: go get -v github.com/tcnksm/ghr
|
||||
command: go install -v github.com/tcnksm/ghr@latest
|
||||
- run:
|
||||
name: publish release on github tag
|
||||
command: ghr -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} ${CIRCLE_TAG} /tmp/ultrasonic-release
|
||||
|
@ -125,11 +137,10 @@ workflows:
|
|||
branches:
|
||||
only:
|
||||
- develop
|
||||
- master
|
||||
- generate_signed_apk:
|
||||
filters:
|
||||
tags:
|
||||
only: /^[0-9]+(\.[0-9]+)*/
|
||||
only: /^[0-9]+(\.[0-9]+)*(-beta\.[0-9]+)?/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
- publish_github_signed_apk:
|
||||
|
@ -137,7 +148,7 @@ workflows:
|
|||
- generate_signed_apk
|
||||
filters:
|
||||
tags:
|
||||
only: /^[0-9]+(\.[0-9]+)*/
|
||||
only: /^[0-9]+(\.[0-9]+)*(-beta\.[0-9]+)?/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ captures/
|
|||
*.iml
|
||||
.idea/
|
||||
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value />
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="&#36;file.fileName Copyright (C) 2009-&#36;today.year Ultrasonic developers Distributed under terms of the GNU GPLv3 license." />
|
||||
<option name="myName" value="Default" />
|
||||
</copyright>
|
||||
</component>
|
|
@ -0,0 +1,7 @@
|
|||
<component name="CopyrightManager">
|
||||
<settings default="Default">
|
||||
<LanguageOptions name="Kotlin">
|
||||
<option name="fileTypeOverride" value="3" />
|
||||
</LanguageOptions>
|
||||
</settings>
|
||||
</component>
|
|
@ -0,0 +1,8 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Reformat" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="processChangedFilesOnly" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK" />
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -19,17 +19,45 @@ By default Pull Request should be opened against **develop** branch, PR against
|
|||
|
||||
1. **License Acceptance:** All contributions must be licensed as [GNU GPLv3](LICENSE) to be accepted.
|
||||
Use `git commit --signoff` to acknowledge this.
|
||||
2. **App is migrating to [Kotlin](https://kotlinlang.org/) programming language:** new Pull Requests
|
||||
should be written in this programming language.
|
||||
3. **No Breakage:** New features or changes to existing ones must not degrade the user experience.
|
||||
4. **Coding standards:** best-practices should be followed, comment generously, and avoid "clever" algorithms.
|
||||
2. **No Breakage:** New features or changes to existing ones must not degrade the user experience.
|
||||
3. **Coding standards:** best-practices should be followed, comment generously, and avoid "clever" algorithms.
|
||||
Refactoring existing messes is great, but watch out for breakage.
|
||||
5. **No large PR:** Try to limit the scope of PR only to the related issue, so it will be easier to review
|
||||
4. **No large PR:** Try to limit the scope of PR only to the related issue, so it will be easier to review
|
||||
and test.
|
||||
|
||||
### Pull Request Process
|
||||
On each Pull Request Github runs a number of checks to make sure there are no problems.
|
||||
|
||||
#### Signed commits
|
||||
Commits must be signed. [See here how to set it up](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
|
||||
|
||||
#### KtLint
|
||||
This programm checks if the source code is formatted correctly.
|
||||
You can run it yourself locally with
|
||||
|
||||
`./gradlew -Pqc ktlintFormat`
|
||||
|
||||
Running this command will fix common problems and will notify you of problems it couldn't fix automatically.
|
||||
|
||||
#### Detekt
|
||||
|
||||
Detekt is a static analyser. It helps to find potential bugs in our code.
|
||||
|
||||
You can run it yourself locally with
|
||||
|
||||
`./gradlew -Pqc detekt`
|
||||
|
||||
There is a "baseline" file, in which errors which have been in the code base before are noted.
|
||||
Sometimes it is necessary to regenerate this file by running:
|
||||
|
||||
`./gradlew -Pqc detektBaseline`
|
||||
|
||||
#### Lint
|
||||
Lint looks for general problems in the code or unused resources etc.
|
||||
You can run it with
|
||||
|
||||
`./gradlew -Pqc lintRelease`
|
||||
|
||||
If there is a need to regenerate the baseline, remove `ultrasonic/lint-baseline.xml` and rerun the command.
|
||||
|
||||
|
||||
1. Ensure [all commits are signed-off](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/about-commit-signature-verification).
|
||||
2. Check tests for the new code are added.
|
||||
3. Check code style is passing.
|
||||
4. Check code static analysis is passing.
|
||||
|
|
|
@ -1,20 +1,28 @@
|
|||
## Problem description
|
||||
|
||||
Describe your problem here. Describe what you want to happen, and what happens
|
||||
if you try to do it. If you have a stack trace or any logs, please format them using
|
||||
github triple backquote notation
|
||||
Describe your problem here. Describe what you want to happen, and what
|
||||
happens if you try to do it. If you have a stack trace or any logs, please
|
||||
format them using GitHub triple backquote notation.
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
Describe how somebody else could observe the same behavior you do. Don't share here any logins and
|
||||
passwords!
|
||||
Describe how somebody else could observe the same behavior you do. Don't
|
||||
share here any logins and passwords!
|
||||
|
||||
## System information
|
||||
|
||||
### Ultrasonic client
|
||||
|
||||
* **Ultrasonic version**: *version of the app*
|
||||
* **Android version**: *Version of Android OS on the device*
|
||||
* **Device info**: *Device manufacturer, model*
|
||||
|
||||
### Server
|
||||
|
||||
* **Server name**: *Airsonic, Ampache, Supysonic...*
|
||||
* **Server version**: *version of server software*
|
||||
* **Protocol used**: *http or https (self certificate, letsencrypt...)*
|
||||
|
||||
## Additional notes
|
||||
|
||||
Include any extra notes here. Otherwise you may remove this section.
|
||||
|
|
62
README.md
62
README.md
|
@ -1,14 +1,25 @@
|
|||
# Ultrasonic
|
||||
[![Build Status](https://circleci.com/gh/ultrasonic/ultrasonic/tree/develop.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/ultrasonic)
|
||||
[![Codecov branch](https://img.shields.io/codecov/c/github/ultrasonic/ultrasonic/develop.svg)]()
|
||||
[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg)](https://ktlint.github.io/)
|
||||
# WE HAVE MOVED
|
||||
|
||||
Ultrasonic is free and open-source music streaming Android client for [Subsonic](http://www.subsonic.org/) [API](http://www.subsonic.org/pages/api.jsp) (version 1.7.0 or higher) compatible servers.
|
||||
Ultrasonic code is now hosted in [GitLab][ultrasonic].
|
||||
|
||||
- New Web: https://ultrasonic.gitlab.io
|
||||
- New Git: https://gitlab.com/ultrasonic/ultrasonic
|
||||
- New bugtracker: https://gitlab.com/ultrasonic/ultrasonic/-/issues
|
||||
- New releases: https://gitlab.com/ultrasonic/ultrasonic/-/packages
|
||||
|
||||
[ultrasonic]: https://gitlab.com/ultrasonic/ultrasonic
|
||||
|
||||
# Ultrasonic
|
||||
|
||||
Ultrasonic is free and open-source music streaming Android client for
|
||||
[Subsonic][subsonic] [API][subapi] (version 1.7.0 or higher) compatible
|
||||
servers.
|
||||
|
||||
## Help wanted
|
||||
|
||||
We currently don't have that much time to spend developing Subsonic, so any
|
||||
contributions or active developers are always welcomed.
|
||||
Have a look at [CONTRIBUTING](CONTRIBUTING.md) to get started.
|
||||
|
||||
## Download
|
||||
|
||||
|
@ -16,24 +27,26 @@ App is available to download at following stores:
|
|||
|
||||
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="70">](https://play.google.com/store/apps/details?id=org.moire.ultrasonic)
|
||||
[<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="70">](https://f-droid.org/packages/org.moire.ultrasonic/)
|
||||
[<img src="https://ultrasonic.github.io/assets/img/get-it-on-github.png" alt="Get it on GitHub" height="70">](https://github.com/ultrasonic/ultrasonic/releases)
|
||||
[<img src="https://ultrasonic.gitlab.io/assets/img/get-it-on-gitlab.png" alt="Get it on GitLab" height="70">](https://gitlab.com/ultrasonic/ultrasonic/-/releases)
|
||||
|
||||
**Warning**: All three versions (Google Play, F-Droid and the APKs) are not
|
||||
compatible (not signed by the same key)! You must uninstall one to install
|
||||
the other, which will delete all your data.
|
||||
|
||||
If you want to use the version downloaded from F-Droid or form Github with **Android Auto**, you must enable Unknown Sources as it is described in [this wiki page](https://github.com/ultrasonic/ultrasonic/wiki/Using-Ultrasonic-with-Android-Auto).
|
||||
If you want to use the version downloaded from F-Droid or from GitLab with
|
||||
**Android Auto**, you must enable Unknown Sources as it is described in
|
||||
[this wiki page][wikiaa].
|
||||
|
||||
## Bugs and issues
|
||||
|
||||
First, see if your issue haven’t been yet reported [here](https://github.com/ultrasonic/ultrasonic/issues),
|
||||
otherwise open [a new issue](https://github.com/ultrasonic/ultrasonic/issues/new).
|
||||
First, see if your issue haven’t been yet reported [here][issues], otherwise
|
||||
open [a new issue][newissue].
|
||||
|
||||
### Known (not our) bugs
|
||||
|
||||
If you are using *Madsonic 5.1.X* several sections of Ultrasonic will not
|
||||
work. This is caused by bad implementation of Subsonic API by Madsonic. For
|
||||
more info about this you can read [this bug](https://github.com/ultrasonic/ultrasonic/issues/129).
|
||||
more info about this you can read [this bug][madbug].
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -41,16 +54,29 @@ See [CONTRIBUTING](CONTRIBUTING.md).
|
|||
|
||||
## Supported (tested) Subsonic API implementations
|
||||
|
||||
- [Subsonic](http://www.subsonic.org/pages/index.jsp)
|
||||
- [Airsonic](https://github.com/airsonic/airsonic)
|
||||
- [Supysonic](https://github.com/spl0k/supysonic)
|
||||
- [Ampache](https://ampache.org/)
|
||||
- [Subsonic][subsonic]
|
||||
- [Airsonic-Advanced][airsonic]
|
||||
- [Supysonic][supysonic]
|
||||
- [Ampache][ampache]
|
||||
|
||||
Other *Subsonic API* implementations should work as well as long as they follow API
|
||||
[documentation](http://www.subsonic.org/pages/api.jsp).
|
||||
Other *Subsonic API* implementations should work as well as long as they
|
||||
follow API [documentation][subapi].
|
||||
|
||||
## License
|
||||
|
||||
This software is licensed under the terms of the GNU General Public License version 3 (GPLv3).
|
||||
This software is licensed under the terms of the GNU General Public License
|
||||
version 3 (GPLv3).
|
||||
|
||||
Full text of the license is available in the [LICENSE](LICENSE) file and [online](https://opensource.org/licenses/gpl-3.0.html).
|
||||
Full text of the license is available in the [LICENSE](LICENSE) file and
|
||||
[online][gpl3].
|
||||
|
||||
[wikiaa]: https://gitlab.com/ultrasonic/ultrasonic/-/wikis/Using-Ultrasonic-with-Android-Auto
|
||||
[issues]: https://gitlab.com/ultrasonic/ultrasonic/-/issues
|
||||
[newissue]: https://gitlab.com/ultrasonic/ultrasonic/-/issues/new
|
||||
[madbug]: https://gitlab.com/ultrasonic/ultrasonic/-/issues/129
|
||||
[subsonic]: http://www.subsonic.org/
|
||||
[subapi]: http://www.subsonic.org/pages/api.jsp
|
||||
[airsonic]: https://github.com/airsonic-advanced/airsonic-advanced
|
||||
[supysonic]: https://github.com/spl0k/supysonic
|
||||
[ampache]: https://ampache.org/
|
||||
[gpl3]: https://opensource.org/licenses/gpl-3.0.html
|
||||
|
|
15
build.gradle
15
build.gradle
|
@ -1,6 +1,6 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
apply from: 'dependencies.gradle'
|
||||
apply from: 'gradle/versions.gradle'
|
||||
|
||||
ext.bootstrap = [
|
||||
kotlinModule : "${project.rootDir}/gradle_scripts/kotlin-module-bootstrap.gradle",
|
||||
|
@ -13,11 +13,10 @@ buildscript {
|
|||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
dependencies {
|
||||
classpath gradlePlugins.gradle
|
||||
classpath gradlePlugins.kotlin
|
||||
classpath gradlePlugins.ktlintGradle
|
||||
classpath gradlePlugins.detekt
|
||||
classpath gradlePlugins.jacoco
|
||||
classpath libs.gradle
|
||||
classpath libs.kotlin
|
||||
classpath libs.ktlintGradle
|
||||
classpath libs.detekt
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,9 +43,7 @@ allprojects {
|
|||
}
|
||||
}
|
||||
|
||||
apply from: 'gradle_scripts/jacoco.gradle'
|
||||
|
||||
wrapper {
|
||||
gradleVersion(versions.gradle)
|
||||
gradleVersion(libs.versions.gradle.get())
|
||||
distributionType("all")
|
||||
}
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
apply from: bootstrap.androidModule
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
ext {
|
||||
jacocoExclude = [
|
||||
'**/domain/**'
|
||||
]
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation androidSupport.roomRuntime
|
||||
implementation androidSupport.roomKtx
|
||||
kapt androidSupport.room
|
||||
implementation libs.roomRuntime
|
||||
implementation libs.roomKtx
|
||||
kapt libs.room
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Album.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import java.util.Date
|
||||
|
||||
@Entity(tableName = "albums", primaryKeys = ["id", "serverId"])
|
||||
data class Album(
|
||||
override var id: String,
|
||||
@ColumnInfo(defaultValue = "-1")
|
||||
override var serverId: Int = -1,
|
||||
override var parent: String? = null,
|
||||
override var album: String? = null,
|
||||
override var title: String? = null,
|
||||
override val name: String? = null,
|
||||
override var discNumber: Int? = 0,
|
||||
override var coverArt: String? = null,
|
||||
override var songCount: Long? = null,
|
||||
override var created: Date? = null,
|
||||
override var artist: String? = null,
|
||||
override var artistId: String? = null,
|
||||
override var duration: Int? = 0,
|
||||
override var year: Int? = 0,
|
||||
override var genre: String? = null,
|
||||
override var starred: Boolean = false,
|
||||
override var path: String? = null,
|
||||
override var closeness: Int = 0,
|
||||
) : MusicDirectory.Child() {
|
||||
override var isDirectory = true
|
||||
override var isVideo = false
|
||||
}
|
|
@ -1,29 +1,23 @@
|
|||
/*
|
||||
* Artist.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "artists")
|
||||
@Entity(tableName = "artists", primaryKeys = ["id", "serverId"])
|
||||
data class Artist(
|
||||
@PrimaryKey override var id: String,
|
||||
override var id: String,
|
||||
@ColumnInfo(defaultValue = "-1")
|
||||
override var serverId: Int = -1,
|
||||
override var name: String? = null,
|
||||
override var index: String? = null,
|
||||
override var coverArt: String? = null,
|
||||
override var albumCount: Long? = null,
|
||||
override var closeness: Int = 0
|
||||
) : ArtistOrIndex(id), Comparable<Artist> {
|
||||
|
||||
override fun compareTo(other: Artist): Int {
|
||||
when {
|
||||
this.closeness == other.closeness -> {
|
||||
return 0
|
||||
}
|
||||
this.closeness > other.closeness -> {
|
||||
return -1
|
||||
}
|
||||
else -> {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
) : ArtistOrIndex(id, serverId)
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
/*
|
||||
* ArtistOrIndex.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.Ignore
|
||||
|
||||
open class ArtistOrIndex(
|
||||
@Suppress("LongParameterList")
|
||||
abstract class ArtistOrIndex(
|
||||
@Ignore
|
||||
override var id: String,
|
||||
@Ignore
|
||||
open var serverId: Int,
|
||||
@Ignore
|
||||
override var name: String? = null,
|
||||
@Ignore
|
||||
open var index: String? = null,
|
||||
|
@ -15,4 +25,21 @@ open class ArtistOrIndex(
|
|||
open var albumCount: Long? = null,
|
||||
@Ignore
|
||||
open var closeness: Int = 0
|
||||
) : GenericEntry()
|
||||
) : GenericEntry() {
|
||||
|
||||
fun compareTo(other: ArtistOrIndex): Int {
|
||||
return when {
|
||||
this.closeness == other.closeness -> {
|
||||
0
|
||||
}
|
||||
this.closeness > other.closeness -> {
|
||||
-1
|
||||
}
|
||||
else -> {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: Identifiable) = compareTo(other as ArtistOrIndex)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.moire.ultrasonic.domain
|
|||
|
||||
import java.io.Serializable
|
||||
import java.util.Date
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry
|
||||
|
||||
data class Bookmark(
|
||||
val position: Int = 0,
|
||||
|
@ -10,7 +9,7 @@ data class Bookmark(
|
|||
val comment: String,
|
||||
val created: Date? = null,
|
||||
val changed: Date? = null,
|
||||
val entry: Entry
|
||||
val track: Track
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = 8988990025189807803L
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.Ignore
|
||||
|
||||
open class GenericEntry {
|
||||
// TODO Should be non-null!
|
||||
@Ignore
|
||||
open val id: String? = null
|
||||
@Ignore
|
||||
open val name: String? = null
|
||||
|
||||
// These are just a formality and will never be called,
|
||||
// because Kotlin data classes will have autogenerated equals() and hashCode() functions
|
||||
override operator fun equals(other: Any?): Boolean {
|
||||
return this === other
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id?.hashCode() ?: 0
|
||||
result = 31 * result + (name?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -7,8 +7,8 @@ import java.io.Serializable
|
|||
@Entity
|
||||
data class Genre(
|
||||
@PrimaryKey val index: String,
|
||||
override val name: String
|
||||
) : Serializable, GenericEntry() {
|
||||
val name: String
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = -3943025175219134028L
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.Ignore
|
||||
|
||||
abstract class GenericEntry : Identifiable {
|
||||
@Ignore
|
||||
open val name: String? = null
|
||||
}
|
||||
|
||||
interface Identifiable : Comparable<Identifiable> {
|
||||
val id: String
|
||||
|
||||
val longId: Long
|
||||
get() = id.hashCode().toLong()
|
||||
|
||||
override fun compareTo(other: Identifiable): Int {
|
||||
return longId.compareTo(other.longId)
|
||||
}
|
||||
}
|
|
@ -1,15 +1,24 @@
|
|||
/*
|
||||
* Index.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "indexes")
|
||||
@Entity(tableName = "indexes", primaryKeys = ["id", "serverId"])
|
||||
data class Index(
|
||||
@PrimaryKey override var id: String,
|
||||
override var id: String,
|
||||
@ColumnInfo(defaultValue = "-1")
|
||||
override var serverId: Int = -1,
|
||||
override var name: String? = null,
|
||||
override var index: String? = null,
|
||||
override var coverArt: String? = null,
|
||||
override var albumCount: Long? = null,
|
||||
override var closeness: Int = 0,
|
||||
var musicFolderId: String? = null
|
||||
) : ArtistOrIndex(id)
|
||||
) : ArtistOrIndex(id, serverId)
|
||||
|
|
|
@ -1,95 +1,61 @@
|
|||
/*
|
||||
* MusicDirectory.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.util.Date
|
||||
|
||||
class MusicDirectory {
|
||||
class MusicDirectory : ArrayList<MusicDirectory.Child>() {
|
||||
var name: String? = null
|
||||
private val children = mutableListOf<Entry>()
|
||||
|
||||
fun addAll(entries: Collection<Entry>) {
|
||||
children.addAll(entries)
|
||||
}
|
||||
|
||||
fun addFirst(child: Entry) {
|
||||
children.add(0, child)
|
||||
}
|
||||
|
||||
fun addChild(child: Entry) {
|
||||
children.add(child)
|
||||
}
|
||||
|
||||
fun findChild(id: String): Entry? = children.lastOrNull { it.id == id }
|
||||
|
||||
fun getAllChild(): List<Entry> = children.toList()
|
||||
|
||||
@JvmOverloads
|
||||
fun getChildren(
|
||||
includeDirs: Boolean = true,
|
||||
includeFiles: Boolean = true
|
||||
): List<Entry> {
|
||||
): List<Child> {
|
||||
if (includeDirs && includeFiles) {
|
||||
return children
|
||||
return toList()
|
||||
}
|
||||
|
||||
return children.filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles }
|
||||
return filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles }
|
||||
}
|
||||
|
||||
@Entity
|
||||
data class Entry(
|
||||
@PrimaryKey override var id: String,
|
||||
var parent: String? = null,
|
||||
var isDirectory: Boolean = false,
|
||||
var title: String? = null,
|
||||
var album: String? = null,
|
||||
var albumId: String? = null,
|
||||
var artist: String? = null,
|
||||
var artistId: String? = null,
|
||||
var track: Int? = 0,
|
||||
var year: Int? = 0,
|
||||
var genre: String? = null,
|
||||
var contentType: String? = null,
|
||||
var suffix: String? = null,
|
||||
var transcodedContentType: String? = null,
|
||||
var transcodedSuffix: String? = null,
|
||||
var coverArt: String? = null,
|
||||
var size: Long? = null,
|
||||
var songCount: Long? = null,
|
||||
var duration: Int? = null,
|
||||
var bitRate: Int? = null,
|
||||
var path: String? = null,
|
||||
var isVideo: Boolean = false,
|
||||
var starred: Boolean = false,
|
||||
var discNumber: Int? = null,
|
||||
var type: String? = null,
|
||||
var created: Date? = null,
|
||||
var closeness: Int = 0,
|
||||
var bookmarkPosition: Int = 0,
|
||||
var userRating: Int? = null,
|
||||
var averageRating: Float? = null
|
||||
) : Serializable, GenericEntry(), Comparable<Entry> {
|
||||
fun setDuration(duration: Long) {
|
||||
this.duration = duration.toInt()
|
||||
fun getTracks(): List<Track> {
|
||||
return mapNotNull {
|
||||
it as? Track
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = -3339106650010798108L
|
||||
fun getAlbums(): List<Album> {
|
||||
return mapNotNull {
|
||||
it as? Album
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: Entry): Int {
|
||||
when {
|
||||
this.closeness == other.closeness -> {
|
||||
return 0
|
||||
}
|
||||
this.closeness > other.closeness -> {
|
||||
return -1
|
||||
}
|
||||
else -> {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
abstract class Child : GenericEntry() {
|
||||
abstract override var id: String
|
||||
abstract var serverId: Int
|
||||
abstract var parent: String?
|
||||
abstract var isDirectory: Boolean
|
||||
abstract var album: String?
|
||||
abstract var title: String?
|
||||
abstract override val name: String?
|
||||
abstract var discNumber: Int?
|
||||
abstract var coverArt: String?
|
||||
abstract var songCount: Long?
|
||||
abstract var created: Date?
|
||||
abstract var artist: String?
|
||||
abstract var artistId: String?
|
||||
abstract var duration: Int?
|
||||
abstract var year: Int?
|
||||
abstract var genre: String?
|
||||
abstract var starred: Boolean
|
||||
abstract var path: String?
|
||||
abstract var closeness: Int
|
||||
abstract var isVideo: Boolean
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
/*
|
||||
* MusicFolder.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
/**
|
||||
* Represents a top level directory in which music or other media is stored.
|
||||
*/
|
||||
@Entity(tableName = "music_folders")
|
||||
@Entity(tableName = "music_folders", primaryKeys = ["id", "serverId"])
|
||||
data class MusicFolder(
|
||||
@PrimaryKey override val id: String,
|
||||
override val name: String
|
||||
override val id: String,
|
||||
override val name: String,
|
||||
@ColumnInfo(defaultValue = "-1")
|
||||
var serverId: Int
|
||||
) : GenericEntry()
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package org.moire.ultrasonic.domain
|
||||
|
||||
enum class PlayerState {
|
||||
IDLE,
|
||||
DOWNLOADING,
|
||||
PREPARING,
|
||||
PREPARED,
|
||||
STARTED,
|
||||
STOPPED,
|
||||
PAUSED,
|
||||
COMPLETED
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package org.moire.ultrasonic.domain
|
||||
|
||||
enum class RepeatMode {
|
||||
OFF {
|
||||
override operator fun next(): RepeatMode = ALL
|
||||
},
|
||||
ALL {
|
||||
override operator fun next(): RepeatMode = SINGLE
|
||||
},
|
||||
SINGLE {
|
||||
override operator fun next(): RepeatMode = OFF
|
||||
};
|
||||
|
||||
abstract operator fun next(): RepeatMode
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
package org.moire.ultrasonic.domain
|
||||
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry
|
||||
|
||||
/**
|
||||
* The result of a search. Contains matching artists, albums and songs.
|
||||
*/
|
||||
data class SearchResult(
|
||||
val artists: List<Artist>,
|
||||
val albums: List<Entry>,
|
||||
val songs: List<Entry>
|
||||
val artists: List<ArtistOrIndex> = listOf(),
|
||||
val albums: List<Album> = listOf(),
|
||||
val songs: List<Track> = listOf()
|
||||
)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package org.moire.ultrasonic.domain
|
||||
|
||||
import java.io.Serializable
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry
|
||||
|
||||
data class Share(
|
||||
override var id: String? = null,
|
||||
override var id: String,
|
||||
var url: String? = null,
|
||||
var description: String? = null,
|
||||
var username: String? = null,
|
||||
|
@ -12,7 +11,7 @@ data class Share(
|
|||
var lastVisited: String? = null,
|
||||
var expires: String? = null,
|
||||
var visitCount: Long? = null,
|
||||
private val entries: MutableList<Entry> = mutableListOf()
|
||||
private val tracks: MutableList<Track> = mutableListOf()
|
||||
) : Serializable, GenericEntry() {
|
||||
override val name: String?
|
||||
get() {
|
||||
|
@ -22,12 +21,12 @@ data class Share(
|
|||
return null
|
||||
}
|
||||
|
||||
fun getEntries(): List<Entry> {
|
||||
return entries.toList()
|
||||
fun getEntries(): List<Track> {
|
||||
return tracks.toList()
|
||||
}
|
||||
|
||||
fun addEntry(entry: Entry) {
|
||||
entries.add(entry)
|
||||
fun addEntry(track: Track) {
|
||||
tracks.add(track)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Track.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.domain
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import java.io.Serializable
|
||||
import java.util.Date
|
||||
|
||||
@Entity(tableName = "tracks", primaryKeys = ["id", "serverId"])
|
||||
data class Track(
|
||||
override var id: String,
|
||||
@ColumnInfo(defaultValue = "-1")
|
||||
override var serverId: Int = -1,
|
||||
override var parent: String? = null,
|
||||
override var isDirectory: Boolean = false,
|
||||
override var title: String? = null,
|
||||
override var album: String? = null,
|
||||
var albumId: String? = null,
|
||||
override var artist: String? = null,
|
||||
override var artistId: String? = null,
|
||||
var track: Int? = null,
|
||||
override var year: Int? = null,
|
||||
override var genre: String? = null,
|
||||
var contentType: String? = null,
|
||||
var suffix: String? = null,
|
||||
var transcodedContentType: String? = null,
|
||||
var transcodedSuffix: String? = null,
|
||||
override var coverArt: String? = null,
|
||||
var size: Long? = null,
|
||||
override var songCount: Long? = null,
|
||||
override var duration: Int? = null,
|
||||
var bitRate: Int? = null,
|
||||
override var path: String? = null,
|
||||
override var isVideo: Boolean = false,
|
||||
override var starred: Boolean = false,
|
||||
override var discNumber: Int? = null,
|
||||
var type: String? = null,
|
||||
override var created: Date? = null,
|
||||
override var closeness: Int = 0,
|
||||
var bookmarkPosition: Int = 0,
|
||||
var userRating: Int? = null,
|
||||
var averageRating: Float? = null,
|
||||
override var name: String? = null
|
||||
) : Serializable, MusicDirectory.Child() {
|
||||
fun setDuration(duration: Long) {
|
||||
this.duration = duration.toInt()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID = -3339106650010798108L
|
||||
}
|
||||
|
||||
fun compareTo(other: Track): Int {
|
||||
when {
|
||||
this.closeness == other.closeness -> {
|
||||
return 0
|
||||
}
|
||||
this.closeness > other.closeness -> {
|
||||
return -1
|
||||
}
|
||||
else -> {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: Identifiable) = compareTo(other as Track)
|
||||
}
|
|
@ -1,30 +1,22 @@
|
|||
apply from: bootstrap.kotlinModule
|
||||
|
||||
dependencies {
|
||||
api other.retrofit
|
||||
api other.jacksonConverter
|
||||
api other.koinCore
|
||||
api libs.retrofit
|
||||
api libs.jacksonConverter
|
||||
api libs.koinCore
|
||||
|
||||
implementation(other.jacksonKotlin) {
|
||||
implementation(libs.jacksonKotlin) {
|
||||
exclude module: 'kotlin-reflect'
|
||||
}
|
||||
implementation other.kotlinReflect // for jackson kotlin, but to use the same version
|
||||
implementation other.okhttpLogging
|
||||
implementation other.timber
|
||||
implementation libs.kotlinReflect // for jackson kotlin, but to use the same version
|
||||
implementation libs.okhttpLogging
|
||||
implementation libs.timber
|
||||
|
||||
testImplementation testing.kotlinJunit
|
||||
testImplementation testing.mockito
|
||||
testImplementation testing.mockitoInline
|
||||
testImplementation testing.mockitoKotlin
|
||||
testImplementation testing.kluent
|
||||
testImplementation testing.mockWebServer
|
||||
testImplementation testing.apacheCodecs
|
||||
}
|
||||
|
||||
ext {
|
||||
// Excluding data classes
|
||||
jacocoExclude = [
|
||||
'**/models/**',
|
||||
'**/di/**'
|
||||
]
|
||||
testImplementation libs.kotlinJunit
|
||||
testImplementation libs.mockito
|
||||
testImplementation libs.mockitoInline
|
||||
testImplementation libs.mockitoKotlin
|
||||
testImplementation libs.kluent
|
||||
testImplementation libs.mockWebServer
|
||||
testImplementation libs.apacheCodecs
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ import java.util.Locale
|
|||
import java.util.TimeZone
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import okio.Okio
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should contain`
|
||||
import org.amshove.kluent.`should not be`
|
||||
|
@ -40,12 +41,12 @@ fun MockWebServer.enqueueResponse(resourceName: String) {
|
|||
}
|
||||
|
||||
fun Any.loadJsonResponse(name: String): String {
|
||||
val source = Okio.buffer(Okio.source(javaClass.classLoader.getResourceAsStream(name)))
|
||||
val source = javaClass.classLoader.getResourceAsStream(name)!!.source().buffer()
|
||||
return source.readString(Charset.forName("UTF-8"))
|
||||
}
|
||||
|
||||
fun Any.loadResourceStream(name: String): InputStream {
|
||||
val source = Okio.buffer(Okio.source(javaClass.classLoader.getResourceAsStream(name)))
|
||||
val source = javaClass.classLoader.getResourceAsStream(name)!!.source().buffer()
|
||||
return source.inputStream()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ package org.moire.ultrasonic.api.subsonic
|
|||
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.BY_GENRE
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration tests for [SubsonicAPIDefinition] for getAlbumList call.
|
||||
|
@ -28,8 +28,8 @@ class SubsonicApiGetAlbumListRequestTest : SubsonicAPIClientTest() {
|
|||
assertResponseSuccessful(response)
|
||||
with(response.body()!!.albumList) {
|
||||
size `should be equal to` 2
|
||||
this[1] `should be equal to` MusicDirectoryChild(
|
||||
id = "9997", parent = "9996", isDir = true,
|
||||
this[1] `should be equal to` Album(
|
||||
id = "9997", parent = "9996",
|
||||
title = "Endless Forms Most Beautiful", album = "Endless Forms Most Beautiful",
|
||||
artist = "Nightwish", year = 2015, genre = "Symphonic Metal",
|
||||
coverArt = "9997", playCount = 11,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectory
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.moire.ultrasonic.api.subsonic
|
|||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
|
||||
|
@ -32,9 +33,8 @@ class SubsonicApiSearchTwoTest : SubsonicAPIClientTest() {
|
|||
artistList.size `should be equal to` 1
|
||||
artistList[0] `should be equal to` Artist(id = "522", name = "The Prodigy")
|
||||
albumList.size `should be equal to` 1
|
||||
albumList[0] `should be equal to` MusicDirectoryChild(
|
||||
id = "8867", parent = "522",
|
||||
isDir = true, title = "Always Outnumbered, Never Outgunned",
|
||||
albumList[0] `should be equal to` Album(
|
||||
id = "8867", parent = "522", title = "Always Outnumbered, Never Outgunned",
|
||||
album = "Always Outnumbered, Never Outgunned", artist = "The Prodigy",
|
||||
year = 2004, genre = "Electronic", coverArt = "8867", playCount = 0,
|
||||
created = parseDate("2016-10-23T20:57:27.000Z")
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ fun Response<out ResponseBody>.toStreamResponse(): StreamResponse {
|
|||
val contentType = responseBody?.contentType()
|
||||
if (
|
||||
contentType != null &&
|
||||
contentType.type().equals("application", true) &&
|
||||
contentType.subtype().equals("json", true)
|
||||
contentType.type.equals("application", true) &&
|
||||
contentType.subtype.equals("json", true)
|
||||
) {
|
||||
val error = SubsonicAPIClient.jacksonMapper.readValue<SubsonicResponse>(
|
||||
responseBody.byteStream()
|
||||
|
@ -40,11 +40,11 @@ fun Response<out ResponseBody>.toStreamResponse(): StreamResponse {
|
|||
* It creates Exceptions from the results returned by the Subsonic API
|
||||
*/
|
||||
@Suppress("ThrowsCount")
|
||||
fun <T : SubsonicResponse> Response<out T>.throwOnFailure(): Response<out T> {
|
||||
fun <T : SubsonicResponse> Response<T>.throwOnFailure(): Response<T> {
|
||||
val response = this
|
||||
|
||||
if (response.isSuccessful && response.body()!!.status === SubsonicResponse.Status.OK) {
|
||||
return this as Response<T>
|
||||
return this
|
||||
}
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("Server error, code: " + response.code())
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.security.cert.X509Certificate
|
|||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.X509TrustManager
|
||||
import okhttp3.Credentials
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.ResponseBody
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
|
@ -68,12 +69,24 @@ class SubsonicAPIClient(
|
|||
.addInterceptor { chain ->
|
||||
// Adds default request params
|
||||
val originalRequest = chain.request()
|
||||
val newUrl = originalRequest.url().newBuilder()
|
||||
val newUrl = originalRequest.url.newBuilder()
|
||||
.addQueryParameter("u", config.username)
|
||||
.addQueryParameter("c", config.clientID)
|
||||
.addQueryParameter("f", "json")
|
||||
.build()
|
||||
chain.proceed(originalRequest.newBuilder().url(newUrl).build())
|
||||
val newRequestBuilder = originalRequest.newBuilder().url(newUrl)
|
||||
if (originalRequest.url.username.isNotEmpty() &&
|
||||
originalRequest.url.password.isNotEmpty()
|
||||
) {
|
||||
newRequestBuilder.addHeader(
|
||||
"Authorization",
|
||||
Credentials.basic(
|
||||
originalRequest.url.username,
|
||||
originalRequest.url.password
|
||||
)
|
||||
)
|
||||
}
|
||||
chain.proceed(newRequestBuilder.build())
|
||||
}
|
||||
.addInterceptor(versionInterceptor)
|
||||
.addInterceptor(proxyPasswordInterceptor)
|
||||
|
@ -83,7 +96,7 @@ class SubsonicAPIClient(
|
|||
|
||||
// Create the Retrofit instance, and register a special converter factory
|
||||
// It will update our protocol version to the correct version, once we made a successful call
|
||||
val retrofit: Retrofit = Retrofit.Builder()
|
||||
private val retrofit: Retrofit = Retrofit.Builder()
|
||||
.baseUrl("${config.baseUrl}/rest/")
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(
|
||||
|
@ -109,14 +122,17 @@ class SubsonicAPIClient(
|
|||
|
||||
private fun OkHttpClient.Builder.addLogging() {
|
||||
val loggingInterceptor = HttpLoggingInterceptor(okLogger)
|
||||
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
|
||||
loggingInterceptor.level = HttpLoggingInterceptor.Level.HEADERS
|
||||
this.addInterceptor(loggingInterceptor)
|
||||
}
|
||||
|
||||
@SuppressWarnings("TrustAllX509TrustManager", "EmptyFunctionBlock")
|
||||
private fun OkHttpClient.Builder.allowSelfSignedCertificates() {
|
||||
val trustManager = object : X509TrustManager {
|
||||
val trustManager =
|
||||
@Suppress("CustomX509TrustManager")
|
||||
object : X509TrustManager {
|
||||
@Suppress("TrustAllX509TrustManager")
|
||||
override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {}
|
||||
@Suppress("TrustAllX509TrustManager")
|
||||
override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {}
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class PasswordHexInterceptor(private val password: String) : Interceptor {
|
|||
|
||||
override fun intercept(chain: Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
val updatedUrl = originalRequest.url().newBuilder()
|
||||
val updatedUrl = originalRequest.url.newBuilder()
|
||||
.addEncodedQueryParameter("p", passwordHex).build()
|
||||
return chain.proceed(originalRequest.newBuilder().url(updatedUrl).build())
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class PasswordMD5Interceptor(private val password: String) : Interceptor {
|
|||
override fun intercept(chain: Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
val salt = getSalt()
|
||||
val updatedUrl = originalRequest.url().newBuilder()
|
||||
val updatedUrl = originalRequest.url.newBuilder()
|
||||
.addQueryParameter("t", getPasswordMD5Hash(salt))
|
||||
.addQueryParameter("s", salt)
|
||||
.build()
|
||||
|
|
|
@ -19,7 +19,7 @@ internal const val TIMEOUT_MILLIS_PER_OFFSET_BYTE = 0.02
|
|||
internal class RangeHeaderInterceptor : Interceptor {
|
||||
override fun intercept(chain: Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
val headers = originalRequest.headers()
|
||||
val headers = originalRequest.headers
|
||||
return if (headers.names().contains("Range")) {
|
||||
val offsetValue = headers["Range"] ?: "0"
|
||||
val offset = "bytes=$offsetValue-"
|
||||
|
|
|
@ -18,7 +18,7 @@ internal class VersionInterceptor(
|
|||
val newRequest = originalRequest.newBuilder()
|
||||
.url(
|
||||
originalRequest
|
||||
.url()
|
||||
.url
|
||||
.newBuilder()
|
||||
.addQueryParameter("v", protocolVersion.restApiVersion)
|
||||
.build()
|
||||
|
|
|
@ -7,8 +7,8 @@ data class Album(
|
|||
val id: String = "",
|
||||
val parent: String = "",
|
||||
val album: String = "",
|
||||
val title: String = "",
|
||||
val name: String = "",
|
||||
val title: String? = null,
|
||||
val name: String? = null,
|
||||
val discNumber: Int = 0,
|
||||
val coverArt: String = "",
|
||||
val songCount: Int = 0,
|
||||
|
@ -18,6 +18,7 @@ data class Album(
|
|||
val duration: Int = 0,
|
||||
val year: Int = 0,
|
||||
val genre: String = "",
|
||||
val playCount: Int = 0,
|
||||
@JsonProperty("song") val songList: List<MusicDirectoryChild> = emptyList(),
|
||||
@JsonProperty("starred") val starredDate: String = ""
|
||||
)
|
||||
|
|
|
@ -4,6 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty
|
|||
|
||||
data class SearchTwoResult(
|
||||
@JsonProperty("artist") val artistList: List<Artist> = emptyList(),
|
||||
@JsonProperty("album") val albumList: List<MusicDirectoryChild> = emptyList(),
|
||||
@JsonProperty("album") val albumList: List<Album> = emptyList(),
|
||||
@JsonProperty("song") val songList: List<MusicDirectoryChild> = emptyList()
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.moire.ultrasonic.api.subsonic.response
|
|||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicError
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
|
||||
class GetAlbumListResponse(
|
||||
status: Status,
|
||||
|
@ -12,10 +12,10 @@ class GetAlbumListResponse(
|
|||
) : SubsonicResponse(status, version, error) {
|
||||
@JsonProperty("albumList") private val albumWrapper = AlbumWrapper()
|
||||
|
||||
val albumList: List<MusicDirectoryChild>
|
||||
val albumList: List<Album>
|
||||
get() = albumWrapper.albumList
|
||||
}
|
||||
|
||||
private class AlbumWrapper(
|
||||
@JsonProperty("album") val albumList: List<MusicDirectoryChild> = emptyList()
|
||||
@JsonProperty("album") val albumList: List<Album> = emptyList()
|
||||
)
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
ext.versions = [
|
||||
minSdk : 14,
|
||||
targetSdk : 29,
|
||||
compileSdk : 29,
|
||||
// You need to run ./gradlew wrapper after updating the version
|
||||
gradle : '7.0',
|
||||
|
||||
navigation : "2.3.5",
|
||||
gradlePlugin : "4.2.2",
|
||||
androidxcore : "1.5.0",
|
||||
ktlint : "0.37.1",
|
||||
ktlintGradle : "9.2.1",
|
||||
detekt : "1.18.1",
|
||||
jacoco : "0.8.7",
|
||||
preferences : "1.1.1",
|
||||
media : "1.3.1",
|
||||
|
||||
androidSupport : "28.0.0",
|
||||
androidLegacySupport : "1.0.0",
|
||||
androidSupportDesign : "1.4.0",
|
||||
constraintLayout : "2.1.1",
|
||||
multidex : "2.0.1",
|
||||
room : "2.3.0",
|
||||
kotlin : "1.5.31",
|
||||
kotlinxCoroutines : "1.5.2-native-mt",
|
||||
viewModelKtx : "2.2.0",
|
||||
|
||||
retrofit : "2.6.4",
|
||||
jackson : "2.9.5",
|
||||
okhttp : "3.12.13",
|
||||
koin : "3.0.2",
|
||||
picasso : "2.71828",
|
||||
sortListView : "1.0.1",
|
||||
|
||||
junit4 : "4.13.2",
|
||||
junit5 : "5.8.1",
|
||||
mockito : "3.12.4",
|
||||
mockitoKotlin : "3.2.0",
|
||||
kluent : "1.68",
|
||||
apacheCodecs : "1.15",
|
||||
robolectric : "4.6.1",
|
||||
dexter : "6.2.3",
|
||||
timber : "4.7.1",
|
||||
fastScroll : "2.0.1",
|
||||
]
|
||||
|
||||
ext.gradlePlugins = [
|
||||
gradle : "com.android.tools.build:gradle:$versions.gradlePlugin",
|
||||
kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin",
|
||||
ktlintGradle : "org.jlleitschuh.gradle:ktlint-gradle:$versions.ktlintGradle",
|
||||
detekt : "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$versions.detekt",
|
||||
jacoco : "org.jacoco:org.jacoco.core:$versions.jacoco",
|
||||
]
|
||||
|
||||
ext.androidSupport = [
|
||||
core : "androidx.core:core-ktx:$versions.androidxcore",
|
||||
support : "androidx.legacy:legacy-support-v4:$versions.androidLegacySupport",
|
||||
design : "com.google.android.material:material:$versions.androidSupportDesign",
|
||||
annotations : "com.android.support:support-annotations:$versions.androidSupport",
|
||||
multidex : "androidx.multidex:multidex:$versions.multidex",
|
||||
constraintLayout : "androidx.constraintlayout:constraintlayout:$versions.constraintLayout",
|
||||
room : "androidx.room:room-compiler:$versions.room",
|
||||
roomRuntime : "androidx.room:room-runtime:$versions.room",
|
||||
roomKtx : "androidx.room:room-ktx:$versions.room",
|
||||
viewModelKtx : "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.viewModelKtx",
|
||||
navigationFragment : "androidx.navigation:navigation-fragment:$versions.navigation",
|
||||
navigationUi : "androidx.navigation:navigation-ui:$versions.navigation",
|
||||
navigationFragmentKtx : "androidx.navigation:navigation-fragment-ktx:$versions.navigation",
|
||||
navigationUiKtx : "androidx.navigation:navigation-ui-ktx:$versions.navigation",
|
||||
navigationFeature : "androidx.navigation:navigation-dynamic-features-fragment:$versions.navigation",
|
||||
preferences : "androidx.preference:preference:$versions.preferences",
|
||||
media : "androidx.media:media:$versions.media",
|
||||
]
|
||||
|
||||
ext.other = [
|
||||
kotlinStdlib : "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin",
|
||||
kotlinReflect : "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlin",
|
||||
kotlinxCoroutines : "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.kotlinxCoroutines",
|
||||
retrofit : "com.squareup.retrofit2:retrofit:$versions.retrofit",
|
||||
gsonConverter : "com.squareup.retrofit2:converter-gson:$versions.retrofit",
|
||||
jacksonConverter : "com.squareup.retrofit2:converter-jackson:$versions.retrofit",
|
||||
jacksonKotlin : "com.fasterxml.jackson.module:jackson-module-kotlin:$versions.jackson",
|
||||
okhttpLogging : "com.squareup.okhttp3:logging-interceptor:$versions.okhttp",
|
||||
koinCore : "io.insert-koin:koin-core:$versions.koin",
|
||||
koinAndroid : "io.insert-koin:koin-android:$versions.koin",
|
||||
koinViewModel : "io.insert-koin:koin-android-viewmodel:$versions.koin",
|
||||
picasso : "com.squareup.picasso:picasso:$versions.picasso",
|
||||
dexter : "com.karumi:dexter:$versions.dexter",
|
||||
timber : "com.jakewharton.timber:timber:$versions.timber",
|
||||
fastScroll : "com.simplecityapps:recyclerview-fastscroll:$versions.fastScroll",
|
||||
sortListView : "com.github.tzugen:drag-sort-listview:$versions.sortListView",
|
||||
]
|
||||
|
||||
ext.testing = [
|
||||
junit : "junit:junit:$versions.junit4",
|
||||
junitVintage : "org.junit.vintage:junit-vintage-engine:$versions.junit5",
|
||||
kotlinJunit : "org.jetbrains.kotlin:kotlin-test-junit:$versions.kotlin",
|
||||
mockitoKotlin : "org.mockito.kotlin:mockito-kotlin:$versions.mockitoKotlin",
|
||||
mockito : "org.mockito:mockito-core:$versions.mockito",
|
||||
mockitoInline : "org.mockito:mockito-inline:$versions.mockito",
|
||||
kluent : "org.amshove.kluent:kluent:$versions.kluent",
|
||||
kluentAndroid : "org.amshove.kluent:kluent-android:$versions.kluent",
|
||||
mockWebServer : "com.squareup.okhttp3:mockwebserver:$versions.okhttp",
|
||||
apacheCodecs : "commons-codec:commons-codec:$versions.apacheCodecs",
|
||||
robolectric : "org.robolectric:robolectric:$versions.robolectric"
|
||||
]
|
|
@ -1,80 +1,26 @@
|
|||
<?xml version="1.0" ?>
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues></ManuallySuppressedIssues>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComplexCondition:DownloadHandler.kt$DownloadHandler.<no name provided>$!append && !playNext && !unpin && !background</ID>
|
||||
<ID>ComplexCondition:FilePickerAdapter.kt$FilePickerAdapter$currentDirectory.absolutePath == "/" || currentDirectory.absolutePath == "/storage" || currentDirectory.absolutePath == "/storage/emulated" || currentDirectory.absolutePath == "/mnt"</ID>
|
||||
<ID>ComplexCondition:LocalMediaPlayer.kt$LocalMediaPlayer$Util.getGaplessPlaybackPreference() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && ( playerState === PlayerState.STARTED || playerState === PlayerState.PAUSED )</ID>
|
||||
<ID>ComplexMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
|
||||
<ID>ComplexMethod:FilePickerAdapter.kt$FilePickerAdapter$private fun fileLister(currentDirectory: File)</ID>
|
||||
<ID>ComplexMethod:SongView.kt$SongView$fun setSong(song: MusicDirectory.Entry, checkable: Boolean, draggable: Boolean)</ID>
|
||||
<ID>ComplexMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun enableButtons()</ID>
|
||||
<ID>ComplexMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory)</ID>
|
||||
<ID>FunctionNaming:ThemeChangedEventDistributor.kt$ThemeChangedEventDistributor$fun RaiseThemeChangedEvent()</ID>
|
||||
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile$String.format("DownloadFile (%s)", song)</ID>
|
||||
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile.DownloadTask$String.format("Download of '%s' was cancelled", song)</ID>
|
||||
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile.DownloadTask$String.format("DownloadTask (%s)", song)</ID>
|
||||
<ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.<no name provided>$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
|
||||
<ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.<no name provided>$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
|
||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Failed to write log to %s", file)</ID>
|
||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Log file rotated, logging into file %s", file?.name)</ID>
|
||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Logging into file %s", file?.name)</ID>
|
||||
<ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$String.format("BufferTask (%s)", downloadFile)</ID>
|
||||
<ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.CheckCompletionTask$String.format("CheckCompletionTask (%s)", downloadFile)</ID>
|
||||
<ID>ImplicitDefaultLocale:ShareHandler.kt$ShareHandler$String.format("%d:%s", timeSpanAmount, timeSpanType)</ID>
|
||||
<ID>ImplicitDefaultLocale:ShareHandler.kt$ShareHandler.<no name provided>$String.format("%s\n\n%s", Util.getShareGreeting(), result.url)</ID>
|
||||
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%02d.", trackNumber)</ID>
|
||||
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s ", bitRate)</ID>
|
||||
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s > %s", suffix, transcodedSuffix)</ID>
|
||||
<ID>LargeClass:TrackCollectionFragment.kt$TrackCollectionFragment : Fragment</ID>
|
||||
<ID>LongMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
|
||||
<ID>LongMethod:EditServerFragment.kt$EditServerFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
|
||||
<ID>LongMethod:FilePickerAdapter.kt$FilePickerAdapter$private fun fileLister(currentDirectory: File)</ID>
|
||||
<ID>LongMethod:LocalMediaPlayer.kt$LocalMediaPlayer$@Synchronized private fun doPlay(downloadFile: DownloadFile, position: Int, start: Boolean)</ID>
|
||||
<ID>LongMethod:NavigationActivity.kt$NavigationActivity$override fun onCreate(savedInstanceState: Bundle?)</ID>
|
||||
<ID>LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken )</ID>
|
||||
<ID>LongMethod:SongView.kt$SongView$fun setSong(song: MusicDirectory.Entry, checkable: Boolean, draggable: Boolean)</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateDisplay(refresh: Boolean)</ID>
|
||||
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory)</ID>
|
||||
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, private var data: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )</ID>
|
||||
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, passedData: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )</ID>
|
||||
<ID>MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192</ID>
|
||||
<ID>MagicNumber:DownloadFile.kt$DownloadFile.DownloadTask$10</ID>
|
||||
<ID>MagicNumber:DownloadFile.kt$DownloadFile.DownloadTask$60</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.<no name provided>$60000</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$100000</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$1024L</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$8</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$86400L</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$8L</ID>
|
||||
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.CheckCompletionTask$5000L</ID>
|
||||
<ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$256</ID>
|
||||
<ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$3</ID>
|
||||
<ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$4</ID>
|
||||
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$0.05f</ID>
|
||||
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$50</ID>
|
||||
<ID>MagicNumber:RESTMusicService.kt$RESTMusicService$206</ID>
|
||||
<ID>MagicNumber:SongView.kt$SongView$3</ID>
|
||||
<ID>MagicNumber:SongView.kt$SongView$4</ID>
|
||||
<ID>MagicNumber:SongView.kt$SongView$60</ID>
|
||||
<ID>MagicNumber:TrackCollectionFragment.kt$TrackCollectionFragment$10</ID>
|
||||
<ID>NestedBlockDepth:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
|
||||
<ID>NestedBlockDepth:DownloadHandler.kt$DownloadHandler$private fun downloadRecursively( fragment: Fragment, id: String, name: String?, isShare: Boolean, isDirectory: Boolean, save: Boolean, append: Boolean, autoPlay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean, isArtist: Boolean )</ID>
|
||||
<ID>NestedBlockDepth:MediaPlayerService.kt$MediaPlayerService$private fun setupOnSongCompletedHandler()</ID>
|
||||
<ID>ReturnCount:CommunicationErrorHandler.kt$CommunicationErrorHandler.Companion$fun getErrorMessage(error: Throwable, context: Context): String</ID>
|
||||
<ID>ReturnCount:ServerRowAdapter.kt$ServerRowAdapter$ private fun popupMenuItemClick(menuItem: MenuItem, position: Int): Boolean</ID>
|
||||
<ID>ReturnCount:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
|
||||
<ID>TooGenericExceptionCaught:DownloadFile.kt$DownloadFile$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:FileLoggerTree.kt$FileLoggerTree$x: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$exception: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$x: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer.PositionCache$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:SongView.kt$SongView$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:SubsonicUncaughtExceptionHandler.kt$SubsonicUncaughtExceptionHandler$x: Throwable</ID>
|
||||
<ID>TooGenericExceptionThrown:DownloadFile.kt$DownloadFile.DownloadTask$throw Exception(String.format("Download of '%s' was cancelled", song))</ID>
|
||||
<ID>TooManyFunctions:MediaPlayerService.kt$MediaPlayerService : Service</ID>
|
||||
<ID>TooGenericExceptionCaught:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$x: Throwable</ID>
|
||||
<ID>TooGenericExceptionCaught:JukeboxMediaPlayer.kt$JukeboxMediaPlayer.TaskQueue$x: Throwable</ID>
|
||||
<ID>TooGenericExceptionThrown:Downloader.kt$Downloader.DownloadTask$throw RuntimeException( String.format( Locale.ROOT, "Download of '%s' was cancelled", downloadFile.track ) )</ID>
|
||||
<ID>TooManyFunctions:RESTMusicService.kt$RESTMusicService : MusicService</ID>
|
||||
<ID>TooManyFunctions:TrackCollectionFragment.kt$TrackCollectionFragment : Fragment</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:CommunicationErrorHandler.kt$CommunicationErrorHandler</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:FragmentTitle.kt$FragmentTitle</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
|
@ -42,8 +42,8 @@ empty-blocks:
|
|||
complexity:
|
||||
active: true
|
||||
TooManyFunctions:
|
||||
thresholdInFiles: 20
|
||||
thresholdInClasses: 20
|
||||
thresholdInFiles: 25
|
||||
thresholdInClasses: 25
|
||||
thresholdInInterfaces: 20
|
||||
thresholdInObjects: 30
|
||||
LabeledExpression:
|
||||
|
@ -64,19 +64,16 @@ style:
|
|||
WildcardImport:
|
||||
active: true
|
||||
MaxLineLength:
|
||||
active: true
|
||||
maxLineLength: 120
|
||||
excludePackageStatements: false
|
||||
excludeImportStatements: false
|
||||
active: false
|
||||
MagicNumber:
|
||||
# 100 common in percentage, 1000 in milliseconds
|
||||
ignoreNumbers: ['-1', '0', '1', '2', '10', '100', '256', '512', '1000', '1024']
|
||||
ignoreNumbers: ['-1', '0', '1', '2', '5', '10', '100', '256', '512', '1000', '1024', '4096']
|
||||
ignoreEnums: true
|
||||
ignorePropertyDeclaration: true
|
||||
UnnecessaryAbstractClass:
|
||||
active: false
|
||||
ReturnCount:
|
||||
max: 3
|
||||
max: 5
|
||||
|
||||
comments:
|
||||
active: true
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Others
|
||||
- #671: Bump versions.mockito from 4.1.0 to 4.3.1.
|
||||
- Update translations.
|
|
@ -0,0 +1,3 @@
|
|||
Enhancements
|
||||
- #683: Rewrite the about and remove the webview.
|
||||
- #685: Server coloring feature.
|
|
@ -0,0 +1,2 @@
|
|||
Bug fixes
|
||||
- #688: Connection failure.
|
|
@ -0,0 +1,14 @@
|
|||
Bug fixes
|
||||
- #673: Disabling "Browse Using ID3 Tags" causes artist search result content to mismatch.
|
||||
- #679: Keyboard should be hidden when navigating away from Search.
|
||||
- #686: In landscape mode, the scroll bar is missing in the about text.
|
||||
- #691: TrackCollectionFragment: fix play all button.
|
||||
- #698: Track based context menus do not function correctly in most fragments.
|
||||
|
||||
Features
|
||||
- #669: Option to change language.
|
||||
|
||||
Enhancements
|
||||
- #654: Update OkHttp.
|
||||
- #694: Reword alert for better help.
|
||||
- #702: Show Downloads on Play.
|
|
@ -1 +0,0 @@
|
|||
Refactor and redesign artist list
|
|
@ -1 +0,0 @@
|
|||
Fall backs to path when comparing tracks and fixes #369
|
|
@ -1 +0,0 @@
|
|||
Refactored the application main menu
|
|
@ -1 +0,0 @@
|
|||
Refactored the application main menu
|
|
@ -9,7 +9,5 @@ Enhancements
|
|||
- #568: Rework Downloader.
|
||||
- #567: Use semantically correct API endpoint when streaming/downloading.
|
||||
- #572: Moved drag handle to the left in the Now Playing list.
|
||||
- #585: Added setting to disable Now Playing List sending for incompatible
|
||||
bluetooth devices.
|
||||
- #596: Added option whether to create a share on the server when sharing
|
||||
songs.
|
||||
- #585: Added setting to disable Now Playing List sending for incompatible bluetooth devices.
|
||||
- #596: Added option whether to create a share on the server when sharing songs.
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
Bug fixes
|
||||
- #609: Weird scrobbling behaviour (offset).
|
||||
|
||||
Enhancements
|
||||
- #599: Moved server selector and settings to the Navigation menu.
|
||||
- #600: Migrate Permission utitlity to Kotlin, increase min SDK to 17.
|
||||
- #604: Implement a Download view.
|
||||
- #613: targetSdkVersion must be 30 or higher.
|
||||
- #622: Refactor events.
|
||||
- #641: Remove feature storage.
|
||||
- #642: Remove MergeAdapter and SackOfViewsAdapter.
|
||||
- #649: Unify error dialog handling.
|
||||
- #652: Updated custom cache location handling to remove isUri.
|
||||
- #662: Improve database migrations.
|
|
@ -0,0 +1,3 @@
|
|||
Otros
|
||||
- #671: Actualizado versions.mockito de 4.1.0 a 4.3.1.
|
||||
- Traducciones actualizadas.
|
|
@ -0,0 +1,3 @@
|
|||
Mejoras
|
||||
- #683: Reescribir el acerca de y eliminar el webview.
|
||||
- #685: Posibilidad de seleccionar el color del servidor.
|
|
@ -0,0 +1,2 @@
|
|||
Corrección de errores
|
||||
- #688: Fallo de conexión.
|
|
@ -0,0 +1,14 @@
|
|||
Corrección de errores
|
||||
- #673: La desactivación de la opción "Examinar mediante etiquetas ID3" hace que el contenido de los resultados de la búsqueda de artistas no coincida.
|
||||
- #679: El teclado debería estar oculto cuando se navega fuera de la Búsqueda.
|
||||
- #686: En modo apaisado, falta la barra de desplazamiento en el texto de acerca de.
|
||||
- #691: TrackCollectionFragment: arreglar el botón de reproducir todo.
|
||||
- #698: Los menús contextuales basados en pistas no funcionan correctamente en la mayoría de los fragmentos.
|
||||
|
||||
Características
|
||||
- #669: Opción de cambio de idioma.
|
||||
|
||||
Mejoras
|
||||
- #654: Actualización de OkHttp.
|
||||
- #694: Reformular la alerta para mejorar la ayuda.
|
||||
- #702: Mostrar descargas al reproducir.
|
|
@ -1 +0,0 @@
|
|||
Refactorizada y rediseñada la lista de artistas
|
|
@ -1 +0,0 @@
|
|||
Cuando un fichero no tiene etiquetas de número de pista lo ordena por orden alfabético. Además hemos arreglado el bug #369
|
|
@ -1 +0,0 @@
|
|||
Refactorizado el menú principal de la aplicación
|
|
@ -1 +0,0 @@
|
|||
Refactorizado el menú principal de la aplicación
|
|
@ -1,6 +1,5 @@
|
|||
Correción de errores
|
||||
- #594: Agregado un intent de PlaybackComplete cuando se completa la
|
||||
reproducción de una canción.
|
||||
- #594: Agregado un intent de PlaybackComplete cuando se completa la reproducción de una canción.
|
||||
- #593: Corregidas las listas de álbumes.
|
||||
- #602: NPE corregido.
|
||||
|
||||
|
@ -8,11 +7,7 @@ Mejoras
|
|||
- #558: La llamada a video puede ser estática.
|
||||
- #559: Agregado un mejor soporte sin conexión.
|
||||
- #568: Se ha reescrito el downloader.
|
||||
- #567: Se utiliza el endpoint semánticamente correcto al realizar streaming
|
||||
o descargar.
|
||||
- #572: Se ha movido el botón de arrastre de canción hacia la izquierda en
|
||||
la lista de reproducción.
|
||||
- #585: Agregada una configuración para deshabilitar el envío de la Lista de
|
||||
reproducción en curso para dispositivos Bluetooth incompatibles.
|
||||
- #596: Se agregó la opción de crear un recurso compartido en el servidor al
|
||||
compartir canciones.
|
||||
- #567: Se utiliza el endpoint semánticamente correcto al realizar streaming o descargar.
|
||||
- #572: Se ha movido el botón de arrastre de canción hacia la izquierda en la lista de reproducción.
|
||||
- #585: Agregada una configuración para deshabilitar el envío de la Lista de reproducción en curso para dispositivos Bluetooth incompatibles.
|
||||
- #596: Se agregó la opción de crear un recurso compartido en el servidor al compartir canciones.
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
Corrección de errores
|
||||
- #609: Comportamiento extraño de scrobbling (offset).
|
||||
|
||||
Mejoras
|
||||
- #599: Se ha movido el selector de servidor y la configuración al menú de navegación.
|
||||
- #600: Migración de la utilidad de permisos a Kotlin, aumento del SDK mínimo a 17.
|
||||
- #604: Implementar una vista de Descarga.
|
||||
- #613: targetSdkVersion debe ser 30 o superior.
|
||||
- #622: Refactorización de eventos.
|
||||
- #641: Eliminar el almacenamiento de funciones.
|
||||
- #642: Eliminar MergeAdapter y SackOfViewsAdapter.
|
||||
- #649: Unificar el manejo del diálogo de error.
|
||||
- #652: Manejo de ubicación de caché personalizado actualizado para eliminar isUri.
|
||||
- #662: Mejorar las migraciones de bases de datos.
|
|
@ -2,7 +2,8 @@ org.gradle.parallel=true
|
|||
org.gradle.daemon=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2g
|
||||
org.gradle.jvmargs=-Xmx2g -XX:+UseParallelGC
|
||||
|
||||
|
||||
kotlin.incremental=true
|
||||
kotlin.caching.enabled=true
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
[versions]
|
||||
# You need to run ./gradlew wrapper after updating the version
|
||||
gradle = "7.3.3"
|
||||
|
||||
navigation = "2.3.5"
|
||||
gradlePlugin = "7.2.1"
|
||||
androidxcore = "1.6.0"
|
||||
ktlint = "0.43.2"
|
||||
ktlintGradle = "10.2.0"
|
||||
detekt = "1.19.0"
|
||||
preferences = "1.1.1"
|
||||
media = "1.3.1"
|
||||
media3 = "1.0.0-beta01"
|
||||
|
||||
androidSupport = "1.4.0"
|
||||
androidLegacySupport = "1.0.0"
|
||||
androidSupportDesign = "1.6.1"
|
||||
constraintLayout = "2.1.1"
|
||||
multidex = "2.0.1"
|
||||
room = "2.4.2"
|
||||
kotlin = "1.6.10"
|
||||
kotlinxCoroutines = "1.6.0-native-mt"
|
||||
kotlinxGuava = "1.6.0"
|
||||
viewModelKtx = "2.4.1"
|
||||
|
||||
retrofit = "2.9.0"
|
||||
jackson = "2.10.1"
|
||||
okhttp = "4.9.1"
|
||||
koin = "3.0.2"
|
||||
picasso = "2.71828"
|
||||
|
||||
junit4 = "4.13.2"
|
||||
junit5 = "5.8.1"
|
||||
mockito = "4.3.1"
|
||||
mockitoKotlin = "4.0.0"
|
||||
kluent = "1.68"
|
||||
apacheCodecs = "1.15"
|
||||
robolectric = "4.6.1"
|
||||
timber = "4.7.1"
|
||||
fastScroll = "2.0.1"
|
||||
colorPicker = "2.2.3"
|
||||
rxJava = "3.1.2"
|
||||
rxAndroid = "3.0.0"
|
||||
multiType = "4.3.0"
|
||||
|
||||
[libraries]
|
||||
gradle = { module = "com.android.tools.build:gradle", version.ref = "gradlePlugin" }
|
||||
kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
ktlintGradle = { module = "org.jlleitschuh.gradle:ktlint-gradle", version.ref = "ktlintGradle" }
|
||||
detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
|
||||
|
||||
core = { module = "androidx.core:core-ktx", version.ref = "androidxcore" }
|
||||
support = { module = "androidx.legacy:legacy-support-v4", version.ref = "androidLegacySupport" }
|
||||
design = { module = "com.google.android.material:material", version.ref = "androidSupportDesign" }
|
||||
annotations = { module = "androidx.annotation:annotation", version.ref = "androidSupport" }
|
||||
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
|
||||
constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintLayout" }
|
||||
room = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
roomRuntime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
roomKtx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
viewModelKtx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "viewModelKtx" }
|
||||
navigationFragment = { module = "androidx.navigation:navigation-fragment", version.ref = "navigation" }
|
||||
navigationUi = { module = "androidx.navigation:navigation-ui", version.ref = "navigation" }
|
||||
navigationFragmentKtx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
|
||||
navigationUiKtx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" }
|
||||
navigationFeature = { module = "androidx.navigation:navigation-dynamic-features-fragment", version.ref = "navigation" }
|
||||
preferences = { module = "androidx.preference:preference", version.ref = "preferences" }
|
||||
media = { module = "androidx.media:media", version.ref = "media" }
|
||||
media3exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
|
||||
media3okhttp = { module = "androidx.media3:media3-datasource-okhttp", version.ref = "media3" }
|
||||
media3session = { module = "androidx.media3:media3-session", version.ref = "media3" }
|
||||
|
||||
kotlinStdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||
kotlinxCoroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
|
||||
kotlinxGuava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "kotlinxGuava"}
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
gsonConverter = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
|
||||
jacksonConverter = { module = "com.squareup.retrofit2:converter-jackson", version.ref = "retrofit" }
|
||||
jacksonKotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
|
||||
okhttpLogging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||
koinCore = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||
koinAndroid = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
||||
koinViewModel = { module = "io.insert-koin:koin-android-viewmodel", version.ref = "koin" }
|
||||
picasso = { module = "com.squareup.picasso:picasso", version.ref = "picasso" }
|
||||
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
||||
fastScroll = { module = "com.simplecityapps:recyclerview-fastscroll", version.ref = "fastScroll" }
|
||||
colorPickerView = { module = "com.github.skydoves:colorpickerview", version.ref = "colorPicker" }
|
||||
rxJava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxJava" }
|
||||
rxAndroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxAndroid" }
|
||||
multiType = { module = "com.drakeet.multitype:multitype", version.ref = "multiType" }
|
||||
|
||||
junit = { module = "junit:junit", version.ref = "junit4" }
|
||||
junitVintage = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" }
|
||||
kotlinJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
||||
mockitoKotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" }
|
||||
mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" }
|
||||
mockitoInline = { module = "org.mockito:mockito-inline", version.ref = "mockito" }
|
||||
kluent = { module = "org.amshove.kluent:kluent", version.ref = "kluent" }
|
||||
kluentAndroid = { module = "org.amshove.kluent:kluent-android", version.ref = "kluent" }
|
||||
mockWebServer = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" }
|
||||
apacheCodecs = { module = "commons-codec:commons-codec", version.ref = "apacheCodecs" }
|
||||
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
|
|
@ -0,0 +1,5 @@
|
|||
ext.versions = [
|
||||
minSdk : 21,
|
||||
targetSdk : 33,
|
||||
compileSdk : 31,
|
||||
]
|
Binary file not shown.
|
@ -1,5 +1,6 @@
|
|||
#Fri Jun 17 23:13:49 CEST 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
*/
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'jacoco'
|
||||
apply from: "${project.rootDir}/gradle_scripts/code_quality.gradle"
|
||||
|
||||
android {
|
||||
|
@ -40,30 +39,21 @@ android {
|
|||
|
||||
buildFeatures {
|
||||
buildConfig = false
|
||||
viewBinding true
|
||||
dataBinding true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tasks.withType(Test) {
|
||||
useJUnitPlatform()
|
||||
jacoco {
|
||||
includeNoLocationClasses = true
|
||||
excludes += jacocoExclude
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api other.kotlinStdlib
|
||||
api libs.kotlinStdlib
|
||||
|
||||
testImplementation testing.junit
|
||||
testRuntimeOnly testing.junitVintage
|
||||
testImplementation libs.junit
|
||||
testRuntimeOnly libs.junitVintage
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion(versions.jacoco)
|
||||
}
|
||||
|
||||
ext {
|
||||
jacocoExclude = ['jdk.internal.*']
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ if (isCodeQualityEnabled) {
|
|||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
|
||||
ktlint {
|
||||
version = versions.ktlint
|
||||
version = libs.versions.ktlint.get()
|
||||
outputToConsole = true
|
||||
android = true
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ if (isCodeQualityEnabled) {
|
|||
|
||||
detekt {
|
||||
buildUponDefaultConfig = true
|
||||
toolVersion = versions.detekt
|
||||
toolVersion = libs.versions.detekt.get()
|
||||
// Builds the AST in parallel. Rules are always executed in parallel.
|
||||
// Can lead to speedups in larger projects.
|
||||
parallel = true
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
apply plugin: 'jacoco'
|
||||
|
||||
jacoco {
|
||||
toolVersion(versions.jacoco)
|
||||
}
|
||||
|
||||
def mergedJacocoExec = file("${project.buildDir}/jacoco/jacocoMerged.exec")
|
||||
|
||||
def merge = tasks.register('jacocoMergeReports', JacocoMerge) {
|
||||
group = "Reporting"
|
||||
description = "Merge all jacoco reports from projects into one."
|
||||
|
||||
ListProperty<File> jacocoFiles = project.objects.listProperty(File.class)
|
||||
project.subprojects { subproject ->
|
||||
subproject.plugins.withId("jacoco") {
|
||||
project.logger.info("${subproject.name} has Jacoco plugin applied")
|
||||
subproject.tasks.withType(Test) { task ->
|
||||
File destFile = task.extensions.getByType(JacocoTaskExtension.class).destinationFile
|
||||
if (destFile.exists() && !task.name.contains("Release")) {
|
||||
jacocoFiles.add(destFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
executionData(jacocoFiles)
|
||||
destinationFile(mergedJacocoExec)
|
||||
}
|
||||
|
||||
tasks.register('jacocoFullReport', JacocoReport) {
|
||||
dependsOn merge
|
||||
group = "Reporting"
|
||||
description = "Generate full Jacoco coverage report including all modules."
|
||||
|
||||
getClassDirectories().setFrom(files())
|
||||
getSourceDirectories().setFrom(files())
|
||||
getExecutionData().setFrom(files())
|
||||
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
csv.enabled = false
|
||||
}
|
||||
|
||||
// Always run merging, as all input calculation is done in doFirst {}
|
||||
outputs.upToDateWhen { false }
|
||||
// Task will run anyway even if initial inputs are empty
|
||||
onlyIf = { true }
|
||||
|
||||
project.subprojects { subproject ->
|
||||
subproject.plugins.withId("jacoco") {
|
||||
project.logger.info("${subproject.name} has Jacoco plugin applied")
|
||||
subproject.plugins.withId("kotlin-android") {
|
||||
project.logger.info("${subproject.name} is android project")
|
||||
def mainSources = subproject.extensions.findByName("android").sourceSets['main']
|
||||
project.logger.info("Android sources: ${mainSources.java.srcDirs}")
|
||||
mainSources.java.srcDirs.forEach {
|
||||
additionalSourceDirs(it)
|
||||
}
|
||||
project.logger.info("Subproject exclude: ${subproject.jacocoExclude}")
|
||||
additionalClassDirs(fileTree(
|
||||
dir: "${subproject.buildDir}/tmp/kotlin-classes/debug",
|
||||
excludes: subproject.jacocoExclude
|
||||
))
|
||||
}
|
||||
subproject.plugins.withId("kotlin") { plugin ->
|
||||
project.logger.info("${subproject.name} is common kotlin project")
|
||||
SourceDirectorySet mainSources = subproject.extensions.getByName("kotlin")
|
||||
.sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
||||
.kotlin
|
||||
mainSources.srcDirs.forEach {
|
||||
project.logger.debug("Adding sources: $it")
|
||||
additionalSourceDirs(it)
|
||||
}
|
||||
project.logger.info("Subproject exclude: ${subproject.jacocoExclude}")
|
||||
additionalClassDirs(fileTree(
|
||||
dir: "${subproject.buildDir}/classes/kotlin/main",
|
||||
excludes: subproject.jacocoExclude
|
||||
))
|
||||
}
|
||||
|
||||
subproject.tasks.withType(Test) { task ->
|
||||
File destFile = task.extensions.getByType(JacocoTaskExtension.class).destinationFile
|
||||
if (destFile.exists() && !task.name.contains("Release")) {
|
||||
project.logger.info("Adding execution data: $destFile")
|
||||
executionData(destFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
*/
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'jacoco'
|
||||
apply from: "${project.rootDir}/gradle_scripts/code_quality.gradle"
|
||||
|
||||
sourceSets {
|
||||
|
@ -15,42 +14,14 @@ sourceSets {
|
|||
|
||||
|
||||
dependencies {
|
||||
api other.kotlinStdlib
|
||||
api libs.kotlinStdlib
|
||||
|
||||
testImplementation testing.junit
|
||||
testRuntimeOnly testing.junitVintage
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion(versions.jacoco)
|
||||
}
|
||||
|
||||
ext {
|
||||
// override it in the module
|
||||
jacocoExclude = ['jdk.internal.*']
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
html.enabled true
|
||||
csv.enabled false
|
||||
xml.enabled true
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
getClassDirectories().setFrom(files(classDirectories.files.collect {
|
||||
fileTree(dir: it, excludes: jacocoExclude)
|
||||
}))
|
||||
}
|
||||
testImplementation libs.junit
|
||||
testRuntimeOnly libs.junitVintage
|
||||
}
|
||||
|
||||
tasks.named("test").configure {
|
||||
useJUnitPlatform()
|
||||
jacoco {
|
||||
excludes += jacocoExclude
|
||||
includeNoLocationClasses = true
|
||||
}
|
||||
finalizedBy jacocoTestReport
|
||||
}
|
||||
|
||||
tasks.register("ciTest") {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,67 +17,101 @@
|
|||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
|
@ -106,80 +140,95 @@ location of your Java installation."
|
|||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
enableFeaturePreview("VERSION_CATALOGS")
|
||||
|
||||
include ':core:domain'
|
||||
include ':core:subsonic-api'
|
||||
include ':ultrasonic'
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'jacoco'
|
||||
apply from: "../gradle_scripts/code_quality.gradle"
|
||||
|
||||
android {
|
||||
|
@ -9,15 +8,16 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId "org.moire.ultrasonic"
|
||||
versionCode 98
|
||||
versionName "2.24.0"
|
||||
versionCode 103
|
||||
versionName "3.2.0"
|
||||
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
|
||||
resConfigs "cs", "de", "en", "es", "fr", "hu", "it", "nl", "pl", "pt", "pt-rBR", "ru", "zh-rCN", "zh-rTW"
|
||||
resConfigs 'cs', 'de', 'en', 'es', 'fr', 'hu', 'it', 'nl', 'pl', 'pt', 'pt-rBR', 'ru', 'zh-rCN', 'zh-rTW'
|
||||
}
|
||||
|
||||
bundle.language.enableSplit = false
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
|
@ -40,23 +40,22 @@ android {
|
|||
main.java.srcDirs += "${projectDir}/src/main/kotlin"
|
||||
test.java.srcDirs += "${projectDir}/src/test/kotlin"
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE'
|
||||
resources {
|
||||
excludes += ['META-INF/LICENSE']
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
baselineFile file("lint-baseline.xml")
|
||||
ignore 'MissingTranslation'
|
||||
warning 'ImpliedQuantity'
|
||||
disable 'IconMissingDensityFolder'
|
||||
abortOnError true
|
||||
warningsAsErrors true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
dataBinding true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -64,9 +63,18 @@ android {
|
|||
|
||||
kapt {
|
||||
arguments {
|
||||
arg("room.schemaLocation", "$buildDir/schemas".toString())
|
||||
arg("room.schemaLocation", "$rootDir/ultrasonic/schemas".toString())
|
||||
}
|
||||
}
|
||||
lint {
|
||||
baseline = file("lint-baseline.xml")
|
||||
abortOnError true
|
||||
warningsAsErrors true
|
||||
disable 'IconMissingDensityFolder', 'VectorPath'
|
||||
ignore 'MissingTranslation', 'UnusedQuantity', 'MissingQuantity'
|
||||
warning 'ImpliedQuantity'
|
||||
disable 'ObsoleteLintCustomCheck'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -78,79 +86,52 @@ dependencies {
|
|||
implementation project(':core:domain')
|
||||
implementation project(':core:subsonic-api')
|
||||
|
||||
api(other.picasso) {
|
||||
api(libs.picasso) {
|
||||
exclude group: "com.android.support"
|
||||
}
|
||||
|
||||
implementation androidSupport.core
|
||||
implementation androidSupport.support
|
||||
implementation androidSupport.design
|
||||
implementation androidSupport.multidex
|
||||
implementation androidSupport.roomRuntime
|
||||
implementation androidSupport.roomKtx
|
||||
implementation androidSupport.viewModelKtx
|
||||
implementation androidSupport.constraintLayout
|
||||
implementation androidSupport.preferences
|
||||
implementation androidSupport.media
|
||||
implementation libs.core
|
||||
implementation libs.support
|
||||
implementation libs.design
|
||||
implementation libs.multidex
|
||||
implementation libs.roomRuntime
|
||||
implementation libs.roomKtx
|
||||
implementation libs.viewModelKtx
|
||||
implementation libs.constraintLayout
|
||||
implementation libs.preferences
|
||||
implementation libs.media
|
||||
implementation libs.media3exoplayer
|
||||
implementation libs.media3session
|
||||
implementation libs.media3okhttp
|
||||
|
||||
implementation androidSupport.navigationFragment
|
||||
implementation androidSupport.navigationUi
|
||||
implementation androidSupport.navigationFragmentKtx
|
||||
implementation androidSupport.navigationUiKtx
|
||||
implementation androidSupport.navigationFeature
|
||||
implementation libs.navigationFragment
|
||||
implementation libs.navigationUi
|
||||
implementation libs.navigationFragmentKtx
|
||||
implementation libs.navigationUiKtx
|
||||
implementation libs.navigationFeature
|
||||
|
||||
implementation other.kotlinStdlib
|
||||
implementation other.kotlinxCoroutines
|
||||
implementation other.koinAndroid
|
||||
implementation other.okhttpLogging
|
||||
implementation other.fastScroll
|
||||
implementation other.sortListView
|
||||
implementation libs.kotlinStdlib
|
||||
implementation libs.kotlinxCoroutines
|
||||
implementation libs.kotlinxGuava
|
||||
implementation libs.koinAndroid
|
||||
implementation libs.okhttpLogging
|
||||
implementation libs.fastScroll
|
||||
implementation libs.colorPickerView
|
||||
implementation libs.rxJava
|
||||
implementation libs.rxAndroid
|
||||
implementation libs.multiType
|
||||
|
||||
kapt androidSupport.room
|
||||
kapt libs.room
|
||||
|
||||
testImplementation other.kotlinReflect
|
||||
testImplementation testing.junit
|
||||
testRuntimeOnly testing.junitVintage
|
||||
testImplementation testing.kotlinJunit
|
||||
testImplementation testing.kluent
|
||||
testImplementation testing.mockito
|
||||
testImplementation testing.mockitoInline
|
||||
testImplementation testing.mockitoKotlin
|
||||
testImplementation testing.robolectric
|
||||
testImplementation libs.kotlinReflect
|
||||
testImplementation libs.junit
|
||||
testRuntimeOnly libs.junitVintage
|
||||
testImplementation libs.kotlinJunit
|
||||
testImplementation libs.kluent
|
||||
testImplementation libs.mockito
|
||||
testImplementation libs.mockitoInline
|
||||
testImplementation libs.mockitoKotlin
|
||||
testImplementation libs.robolectric
|
||||
|
||||
implementation other.dexter
|
||||
implementation other.timber
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion(versions.jacoco)
|
||||
}
|
||||
|
||||
// Excluding all java classes and stuff that should not be covered
|
||||
ext {
|
||||
jacocoExclude = [
|
||||
'**/activity/**',
|
||||
'**/audiofx/**',
|
||||
'**/fragment/**',
|
||||
'**/provider/**',
|
||||
'**/receiver/**',
|
||||
'**/service/**',
|
||||
'**/Test/**',
|
||||
'**/util/**',
|
||||
'**/view/**',
|
||||
'**/R$*.class',
|
||||
'**/R.class',
|
||||
'**/BuildConfig.class',
|
||||
'**/di/**',
|
||||
'jdk.internal.*'
|
||||
]
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion(versions.jacoco)
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
jacoco.includeNoLocationClasses = true
|
||||
jacoco.excludes += jacocoExclude
|
||||
implementation libs.timber
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,124 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 5,
|
||||
"identityHash": "4cea788a99b9bc28500948b1cd92e537",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "ServerSetting",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `index` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `color` INTEGER, `userName` TEXT NOT NULL, `password` TEXT NOT NULL, `jukeboxByDefault` INTEGER NOT NULL, `allowSelfSignedCertificate` INTEGER NOT NULL, `ldapSupport` INTEGER NOT NULL, `musicFolderId` TEXT, `minimumApiVersion` TEXT, `chatSupport` INTEGER, `bookmarkSupport` INTEGER, `shareSupport` INTEGER, `podcastSupport` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "userName",
|
||||
"columnName": "userName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "password",
|
||||
"columnName": "password",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "jukeboxByDefault",
|
||||
"columnName": "jukeboxByDefault",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "allowSelfSignedCertificate",
|
||||
"columnName": "allowSelfSignedCertificate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "ldapSupport",
|
||||
"columnName": "ldapSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "musicFolderId",
|
||||
"columnName": "musicFolderId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "minimumApiVersion",
|
||||
"columnName": "minimumApiVersion",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatSupport",
|
||||
"columnName": "chatSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkSupport",
|
||||
"columnName": "bookmarkSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "shareSupport",
|
||||
"columnName": "shareSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "podcastSupport",
|
||||
"columnName": "podcastSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"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, '4cea788a99b9bc28500948b1cd92e537')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "0580217b1e87b02d2edaf9b008891cbc",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "artists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "indexes",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, `musicFolderId` TEXT, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "musicFolderId",
|
||||
"columnName": "musicFolderId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "music_folders",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"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, '0580217b1e87b02d2edaf9b008891cbc')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,474 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "b6ac795e7857eac4fed2dbbd01f80fb8",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "artists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "albums",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent` TEXT, `album` TEXT, `title` TEXT, `name` TEXT, `discNumber` INTEGER, `coverArt` TEXT, `songCount` INTEGER, `created` INTEGER, `artist` TEXT, `artistId` TEXT, `duration` INTEGER, `year` INTEGER, `genre` TEXT, `starred` INTEGER NOT NULL, `path` TEXT, `closeness` INTEGER NOT NULL, `isDirectory` INTEGER NOT NULL, `isVideo` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parent",
|
||||
"columnName": "parent",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "discNumber",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "songCount",
|
||||
"columnName": "songCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "isVideo",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "tracks",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `parent` TEXT, `isDirectory` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `albumId` TEXT, `artist` TEXT, `artistId` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `contentType` TEXT, `suffix` TEXT, `transcodedContentType` TEXT, `transcodedSuffix` TEXT, `coverArt` TEXT, `size` INTEGER, `songCount` INTEGER, `duration` INTEGER, `bitRate` INTEGER, `path` TEXT, `isVideo` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `discNumber` INTEGER, `type` TEXT, `created` INTEGER, `closeness` INTEGER NOT NULL, `bookmarkPosition` INTEGER NOT NULL, `userRating` INTEGER, `averageRating` REAL, `name` TEXT, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parent",
|
||||
"columnName": "parent",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "albumId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"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": "contentType",
|
||||
"columnName": "contentType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcodedContentType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcodedSuffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "songCount",
|
||||
"columnName": "songCount",
|
||||
"affinity": "INTEGER",
|
||||
"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": "isVideo",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "discNumber",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmarkPosition",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "userRating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "averageRating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "indexes",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, `musicFolderId` TEXT, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "musicFolderId",
|
||||
"columnName": "musicFolderId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "music_folders",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"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, 'b6ac795e7857eac4fed2dbbd01f80fb8')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,514 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "95e83d6663a862c03ac46f9567453ded",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "artists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, PRIMARY KEY(`id`, `serverId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "serverId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "-1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id",
|
||||
"serverId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "albums",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `parent` TEXT, `album` TEXT, `title` TEXT, `name` TEXT, `discNumber` INTEGER, `coverArt` TEXT, `songCount` INTEGER, `created` INTEGER, `artist` TEXT, `artistId` TEXT, `duration` INTEGER, `year` INTEGER, `genre` TEXT, `starred` INTEGER NOT NULL, `path` TEXT, `closeness` INTEGER NOT NULL, `isDirectory` INTEGER NOT NULL, `isVideo` INTEGER NOT NULL, PRIMARY KEY(`id`, `serverId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "serverId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "-1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "parent",
|
||||
"columnName": "parent",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "discNumber",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "songCount",
|
||||
"columnName": "songCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "duration",
|
||||
"columnName": "duration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "year",
|
||||
"columnName": "year",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "genre",
|
||||
"columnName": "genre",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "path",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isVideo",
|
||||
"columnName": "isVideo",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id",
|
||||
"serverId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "tracks",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `parent` TEXT, `isDirectory` INTEGER NOT NULL, `title` TEXT, `album` TEXT, `albumId` TEXT, `artist` TEXT, `artistId` TEXT, `track` INTEGER, `year` INTEGER, `genre` TEXT, `contentType` TEXT, `suffix` TEXT, `transcodedContentType` TEXT, `transcodedSuffix` TEXT, `coverArt` TEXT, `size` INTEGER, `songCount` INTEGER, `duration` INTEGER, `bitRate` INTEGER, `path` TEXT, `isVideo` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `discNumber` INTEGER, `type` TEXT, `created` INTEGER, `closeness` INTEGER NOT NULL, `bookmarkPosition` INTEGER NOT NULL, `userRating` INTEGER, `averageRating` REAL, `name` TEXT, PRIMARY KEY(`id`, `serverId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "serverId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "-1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "parent",
|
||||
"columnName": "parent",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "isDirectory",
|
||||
"columnName": "isDirectory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "album",
|
||||
"columnName": "album",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumId",
|
||||
"columnName": "albumId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "artistId",
|
||||
"columnName": "artistId",
|
||||
"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": "contentType",
|
||||
"columnName": "contentType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "suffix",
|
||||
"columnName": "suffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedContentType",
|
||||
"columnName": "transcodedContentType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "transcodedSuffix",
|
||||
"columnName": "transcodedSuffix",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "size",
|
||||
"columnName": "size",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "songCount",
|
||||
"columnName": "songCount",
|
||||
"affinity": "INTEGER",
|
||||
"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": "isVideo",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "discNumber",
|
||||
"columnName": "discNumber",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "created",
|
||||
"columnName": "created",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkPosition",
|
||||
"columnName": "bookmarkPosition",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userRating",
|
||||
"columnName": "userRating",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "averageRating",
|
||||
"columnName": "averageRating",
|
||||
"affinity": "REAL",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id",
|
||||
"serverId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "indexes",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT, `index` TEXT, `coverArt` TEXT, `albumCount` INTEGER, `closeness` INTEGER NOT NULL, `musicFolderId` TEXT, PRIMARY KEY(`id`, `serverId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "serverId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "-1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "coverArt",
|
||||
"columnName": "coverArt",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "albumCount",
|
||||
"columnName": "albumCount",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "closeness",
|
||||
"columnName": "closeness",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "musicFolderId",
|
||||
"columnName": "musicFolderId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id",
|
||||
"serverId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "music_folders",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `serverId` INTEGER NOT NULL DEFAULT -1, `name` TEXT NOT NULL, PRIMARY KEY(`id`, `serverId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverId",
|
||||
"columnName": "serverId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "-1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id",
|
||||
"serverId"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"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, '95e83d6663a862c03ac46f9567453ded')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
|
@ -22,12 +21,17 @@
|
|||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="@xml/backup_descriptor"
|
||||
android:dataExtractionRules="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/NoActionBar"
|
||||
android:name=".app.UApp"
|
||||
android:label="@string/common.appname"
|
||||
android:usesCleartextTraffic="true">
|
||||
android:usesCleartextTraffic="true"
|
||||
android:supportsRtl="false"
|
||||
android:preserveLegacyExternalStorage="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<meta-data android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/automotive_app_desc"/>
|
||||
|
@ -38,8 +42,8 @@
|
|||
|
||||
<activity android:name=".activity.NavigationActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/common.appname"
|
||||
android:launchMode="singleTask">
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.SEARCH"/>
|
||||
|
@ -55,28 +59,25 @@
|
|||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.MediaPlayerService"
|
||||
android:name=".service.DownloadService"
|
||||
android:label="Ultrasonic Media Player Service"
|
||||
android:exported="false">
|
||||
</service>
|
||||
|
||||
<service
|
||||
tools:ignore="ExportedService"
|
||||
android:name=".service.AutoMediaBrowserService"
|
||||
<!-- Needs to be exported: https://android.googlesource.com/platform/developers/build/+/4de32d4/prebuilts/gradle/MediaBrowserService/README.md -->
|
||||
<service android:name=".playback.PlaybackService"
|
||||
android:label="@string/common.appname"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaLibraryService" />
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".receiver.MediaButtonIntentReceiver">
|
||||
<intent-filter android:priority="2147483647">
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".receiver.UltrasonicIntentReceiver">
|
||||
<receiver android:name=".receiver.UltrasonicIntentReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="org.moire.ultrasonic.CMD_TOGGLEPAUSE"/>
|
||||
<action android:name="org.moire.ultrasonic.CMD_PLAY"/>
|
||||
|
@ -88,7 +89,8 @@
|
|||
<action android:name="org.moire.ultrasonic.CMD_PROCESS_KEYCODE"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".receiver.BluetoothIntentReceiver">
|
||||
<receiver android:name=".receiver.BluetoothIntentReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.bluetooth.device.action.ACL_CONNECTED"/>
|
||||
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
|
||||
|
@ -98,7 +100,8 @@
|
|||
</receiver>
|
||||
<receiver
|
||||
android:name=".provider.UltrasonicAppWidgetProvider4X1"
|
||||
android:label="Ultrasonic (4x1)">
|
||||
android:label="Ultrasonic (4x1)"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||
</intent-filter>
|
||||
|
@ -109,7 +112,8 @@
|
|||
</receiver>
|
||||
<receiver
|
||||
android:name=".provider.UltrasonicAppWidgetProvider4X2"
|
||||
android:label="Ultrasonic (4x2)">
|
||||
android:label="Ultrasonic (4x2)"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||
</intent-filter>
|
||||
|
@ -120,7 +124,8 @@
|
|||
</receiver>
|
||||
<receiver
|
||||
android:name=".provider.UltrasonicAppWidgetProvider4X3"
|
||||
android:label="Ultrasonic (4x3)">
|
||||
android:label="Ultrasonic (4x3)"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||
</intent-filter>
|
||||
|
@ -131,7 +136,8 @@
|
|||
</receiver>
|
||||
<receiver
|
||||
android:name=".provider.UltrasonicAppWidgetProvider4X4"
|
||||
android:label="Ultrasonic (4x4)">
|
||||
android:label="Ultrasonic (4x4)"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||
</intent-filter>
|
||||
|
@ -140,18 +146,17 @@
|
|||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/appwidget_info_4x4"/>
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:name=".provider.SearchSuggestionProvider"
|
||||
android:authorities="org.moire.ultrasonic.provider.SearchSuggestionProvider"/>
|
||||
|
||||
<receiver
|
||||
android:name=".receiver.A2dpIntentReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.music.playstatusrequest"/>
|
||||
<receiver android:name=".receiver.MediaButtonIntentReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter android:priority="2147483647">
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<provider
|
||||
android:name=".provider.SearchSuggestionProvider"
|
||||
android:authorities="org.moire.ultrasonic.provider.SearchSuggestionProvider"
|
||||
android:exported="true" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>About Ultrasonic</title>
|
||||
<link rel="stylesheet" href="../style.css" type="text/css">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h3><img src="../img/ultrasonic.png" alt="">Ultrasonic</h3>
|
||||
|
||||
<p>
|
||||
Mit <b>Ultrasonic</b> können Sie mit dem Subsonic Media Streamer ganz einfach Musik von Ihrem Heimcomputer auf Ihr Android-Handy streamen oder
|
||||
herunterladen. Die Subsonic-Server-Software erfordert eine zusätzliche, von Ultrasonic getrennte Konfiguration. Für weitere Informationen oder
|
||||
zur Installation der Subsonic-Server-Software auf Ihrem Computer besuchen Sie bitte <a href="http://subsonic.org">subsonic.org</a>. Die Basisversion
|
||||
von Subsonic ist kostenlos. Wenn Sie Subsonic zum ersten Mal installieren, sind die Premium-Funktionen 30 Tage lang verfügbar, so dass Sie sie
|
||||
ausprobieren können, bevor Sie sich für ein Upgrade entscheiden. Klicken Sie <a href="http://www.subsonic.org/pages/premium.jsp">hier</a>, um
|
||||
ein Upgrade auf Subsonic Premium durchzuführen.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Standardmäßig ist Ultrasonic nicht konfiguriert. Wenn Sie Ihren eigenen Server eingerichtet haben, gehen Sie bitte zu <b>Einstellungen</b> und
|
||||
ändern Sie die Konfiguration so, dass er mit Ihrem eigenen Computer verbunden wird.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Wenn Sie mit Ultrasonic zufrieden sind, klicken Sie bitte auf "Spenden", um die weitere Entwicklung zu unterstützen. Diese Spende ist von der
|
||||
Subsonic-Server-Software getrennt und gewährt Ihnen keinen Zugang zu den Premium-Funktionen von Subsonic.
|
||||
</p>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="DQXEZRDRAGCA8">
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
<p>
|
||||
Um Feature-Anfragen oder Fehlerberichte einzureichen, besuchen Sie bitte das Ultrasonic für Android <a href="http://forum.subsonic.org/forum/viewforum.php?f=17">Forum</a>.
|
||||
Der Quellcode von Ultrasonic ist unter <a href="https://github.com/ogarcia/ultrasonic">github.com</a> verfügbar und unter den Bedingungen der GNU General Public License Version 3 (GPLv3) lizenziert.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,52 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>About Ultrasonic</title>
|
||||
<link rel="stylesheet" href="../style.css" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h3><img src="../img/ultrasonic.png" alt="">Ultrasonic</h3>
|
||||
|
||||
<p>
|
||||
With <b>Ultrasonic</b> you can easily stream or download music from your
|
||||
home computer to your Android phone using the Subsonic media streamer.
|
||||
The Subsonic server software requires additional configuration separate
|
||||
from Ultrasonic. For more information or to install the Subsonic server
|
||||
software on your computer, please visit
|
||||
<a href="http://subsonic.org">subsonic.org</a>. The basic version of
|
||||
Subsonic is free. When you first install Subsonic, the premium features
|
||||
are available for 30 days so you can try them out before deciding to
|
||||
upgrade. Click
|
||||
<a href="http://www.subsonic.org/pages/premium.jsp">here</a> to upgrade
|
||||
to Subsonic Premium.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
By default, Ultrasonic is not configured. Once you've set up your own
|
||||
server, please go to <b>Settings</b> and change the configuration so
|
||||
that it connects to your own computer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are pleased with Ultrasonic, please click "Donate" to help
|
||||
further development. This donation is separate from the Subsonic server
|
||||
software and does not grant you access to the premium features of
|
||||
Subsonic.
|
||||
</p>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="DQXEZRDRAGCA8">
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
<p>
|
||||
To submit feature requests or file bug reports, please visit the
|
||||
Ultrasonic for Android
|
||||
<a href="http://forum.subsonic.org/forum/viewforum.php?f=17">forum</a>.
|
||||
Source code for Ultrasonic is available at
|
||||
<a href="https://github.com/ogarcia/ultrasonic">github.com</a>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,61 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Aide de Ultrasonic</title>
|
||||
<link rel="stylesheet" href="../style.css" type="text/css">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h3><img src="../img/ultrasonic.png" alt=""> Bienvenue dans Ultrasonic</h3>
|
||||
|
||||
<p>
|
||||
Avec <b>Ultrasonic</b>, vous pouvez facilement écouter ou télécharger de la musique à partir de votre ordinateur personnel sur votre appareil Android
|
||||
(et bien d'autres choses sont possibles).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pour installer le serveur Subsonic sur votre ordinateur, visitez <a href="http://subsonic.org">subsonic.org</a>.
|
||||
Celui-ci est disponible pour Windows, Mac, Linux et Unix.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Par défaut, cette application n'est pas configurée. Après avoir configuré votre
|
||||
serveur personnel, veuillez accéder aux <b>Paramètres</b> et modifier la configuration afin de vous connecter à votre propre ordinateur ou vos appareils mobiles.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Vous pouvez utiliser cette application gratuitement pendant 30 jours.
|
||||
Ensuite, vous devrez effectuer un don au projet Subsonic.
|
||||
En tant que donateur, vous obtiendrez les bénéfices suivants:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Écoute et téléchargement illimités vers autant de iPhones et d'appareils Android que souhaité.</li>
|
||||
<li>Lecture de vidéos.</li>
|
||||
<li>Une adresse web personnalisée pour votre serveur Subsonic (<em>votrenom</em>.subsonic.org).</li>
|
||||
<li>Aucunes publicités dans l'interface web de Subsonic.</li>
|
||||
<li>Accès gratuit aux nouvelles fonctionnalités avancées.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Le montant suggéré pour le don est de <b>20€</b>, mais n'importe quel montant est traité et accepté.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Cliquez sur le bouton suivants pour accéder à PayPal, d'où vous pourrez payer soit par carte de crédit, soit en utilisant votre compte PayPal.
|
||||
Une fois le don reçu et traité, vous recevrez votre clé d'activation par e-mail.
|
||||
</p>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="DQXEZRDRAGCA8">
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online!">
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
<p>
|
||||
Pour plus d'information, veuiller visitez <a href="http://subsonic.org/">subsonic.org</a>. Le code source de Ultrasonic est disponible à l'adresse suivante : <a href="https://github.com/ogarcia/ultrasonic">github.com</a>.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB |
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* Taken from http://yui.yahooapis.com/2.8.0r4/build/fonts/fonts.css
|
||||
*/
|
||||
body {
|
||||
font: 13px / 1.231 arial, helvetica, clean, sans-serif;
|
||||
}
|
||||
|
||||
table {
|
||||
font-size:inherit;
|
||||
font:100%;
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
/**
|
||||
* Displays online help and about information in a WebView
|
||||
*/
|
||||
public class AboutFragment extends Fragment {
|
||||
|
||||
private WebView webView;
|
||||
private ImageView backButton;
|
||||
private ImageView forwardButton;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.help, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
swipeRefresh = view.findViewById(R.id.help_refresh);
|
||||
swipeRefresh.setEnabled(false);
|
||||
|
||||
webView = view.findViewById(R.id.help_contents);
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.setWebViewClient(new HelpClient());
|
||||
|
||||
if (savedInstanceState != null)
|
||||
{
|
||||
webView.restoreState(savedInstanceState);
|
||||
}
|
||||
else
|
||||
{
|
||||
webView.loadUrl(getResources().getString(R.string.help_url));
|
||||
}
|
||||
|
||||
backButton = view.findViewById(R.id.help_back);
|
||||
backButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.goBack();
|
||||
}
|
||||
});
|
||||
|
||||
ImageView stopButton = view.findViewById(R.id.help_stop);
|
||||
stopButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.stopLoading();
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
|
||||
forwardButton = view.findViewById(R.id.help_forward);
|
||||
forwardButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.goForward();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Nicer Back key handling?
|
||||
webView.setFocusableInTouchMode(true);
|
||||
webView.requestFocus();
|
||||
webView.setOnKeyListener( new View.OnKeyListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onKey( View v, int keyCode, KeyEvent event )
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
if (webView.canGoBack())
|
||||
{
|
||||
webView.goBack();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NotNull Bundle state)
|
||||
{
|
||||
webView.saveState(state);
|
||||
super.onSaveInstanceState(state);
|
||||
}
|
||||
|
||||
private final class HelpClient extends WebViewClient
|
||||
{
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
swipeRefresh.setRefreshing(true);
|
||||
super.onPageStarted(view, url, favicon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url)
|
||||
{
|
||||
swipeRefresh.setRefreshing(false);
|
||||
String versionName = Util.getVersionName(getContext());
|
||||
String title = String.format("%s (%s)", view.getTitle(), versionName);
|
||||
|
||||
FragmentTitle.Companion.setTitle(AboutFragment.this, title);
|
||||
|
||||
backButton.setEnabled(view.canGoBack());
|
||||
forwardButton.setEnabled(view.canGoForward());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
|
||||
{
|
||||
Util.toast(getContext(), description);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,387 +0,0 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.service.DownloadFile;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker;
|
||||
import org.moire.ultrasonic.subsonic.VideoPlayer;
|
||||
import org.moire.ultrasonic.util.CancellationToken;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.FragmentBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Pair;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.EntryAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Lazy;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
/**
|
||||
* Lists the Bookmarks available on the server
|
||||
*/
|
||||
public class BookmarksFragment extends Fragment {
|
||||
|
||||
private SwipeRefreshLayout refreshAlbumListView;
|
||||
private ListView albumListView;
|
||||
private View albumButtons;
|
||||
private View emptyView;
|
||||
private ImageView playNowButton;
|
||||
private ImageView pinButton;
|
||||
private ImageView unpinButton;
|
||||
private ImageView downloadButton;
|
||||
private ImageView deleteButton;
|
||||
|
||||
private final Lazy<MediaPlayerController> mediaPlayerController = inject(MediaPlayerController.class);
|
||||
private final Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
|
||||
private final Lazy<NetworkAndStorageChecker> networkAndStorageChecker = inject(NetworkAndStorageChecker.class);
|
||||
private CancellationToken cancellationToken;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.select_album, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
cancellationToken = new CancellationToken();
|
||||
albumButtons = view.findViewById(R.id.menu_album);
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh);
|
||||
albumListView = view.findViewById(R.id.select_album_entries_list);
|
||||
|
||||
refreshAlbumListView.setOnRefreshListener(() -> {
|
||||
enableButtons();
|
||||
getBookmarks();
|
||||
});
|
||||
|
||||
albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
|
||||
albumListView.setOnItemClickListener((parent, view17, position, id) -> {
|
||||
if (position >= 0)
|
||||
{
|
||||
MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position);
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
if (entry.isVideo())
|
||||
{
|
||||
VideoPlayer.Companion.playVideo(getContext(), entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
enableButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ImageView selectButton = view.findViewById(R.id.select_album_select);
|
||||
playNowButton = view.findViewById(R.id.select_album_play_now);
|
||||
ImageView playNextButton = view.findViewById(R.id.select_album_play_next);
|
||||
ImageView playLastButton = view.findViewById(R.id.select_album_play_last);
|
||||
pinButton = view.findViewById(R.id.select_album_pin);
|
||||
unpinButton = view.findViewById(R.id.select_album_unpin);
|
||||
downloadButton = view.findViewById(R.id.select_album_download);
|
||||
deleteButton = view.findViewById(R.id.select_album_delete);
|
||||
ImageView oreButton = view.findViewById(R.id.select_album_more);
|
||||
emptyView = view.findViewById(R.id.select_album_empty);
|
||||
|
||||
selectButton.setVisibility(View.GONE);
|
||||
playNextButton.setVisibility(View.GONE);
|
||||
playLastButton.setVisibility(View.GONE);
|
||||
oreButton.setVisibility(View.GONE);
|
||||
|
||||
playNowButton.setOnClickListener(view16 -> playNow(getSelectedSongs(albumListView)));
|
||||
|
||||
selectButton.setOnClickListener(view15 -> selectAllOrNone());
|
||||
pinButton.setOnClickListener(view14 -> {
|
||||
downloadBackground(true);
|
||||
selectAll(false, false);
|
||||
});
|
||||
unpinButton.setOnClickListener(view13 -> {
|
||||
unpin();
|
||||
selectAll(false, false);
|
||||
});
|
||||
downloadButton.setOnClickListener(view12 -> {
|
||||
downloadBackground(false);
|
||||
selectAll(false, false);
|
||||
});
|
||||
deleteButton.setOnClickListener(view1 -> {
|
||||
delete();
|
||||
selectAll(false, false);
|
||||
});
|
||||
|
||||
registerForContextMenu(albumListView);
|
||||
FragmentTitle.Companion.setTitle(this, R.string.button_bar_bookmarks);
|
||||
|
||||
enableButtons();
|
||||
getBookmarks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
cancellationToken.cancel();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void getBookmarks()
|
||||
{
|
||||
new LoadTask()
|
||||
{
|
||||
@Override
|
||||
protected MusicDirectory load(MusicService service) throws Exception
|
||||
{
|
||||
return Util.getSongsFromBookmarks(service.getBookmarks());
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void playNow(List<MusicDirectory.Entry> songs)
|
||||
{
|
||||
if (!getSelectedSongs(albumListView).isEmpty())
|
||||
{
|
||||
int position = songs.get(0).getBookmarkPosition();
|
||||
mediaPlayerController.getValue().restore(songs, 0, position, true, true);
|
||||
selectAll(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView)
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = new ArrayList<>(10);
|
||||
|
||||
if (albumListView != null)
|
||||
{
|
||||
int count = albumListView.getCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (albumListView.isItemChecked(i))
|
||||
{
|
||||
MusicDirectory.Entry song = (MusicDirectory.Entry) albumListView.getItemAtPosition(i);
|
||||
if (song != null) songs.add(song);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return songs;
|
||||
}
|
||||
|
||||
private void selectAllOrNone()
|
||||
{
|
||||
boolean someUnselected = false;
|
||||
int count = albumListView.getCount();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!albumListView.isItemChecked(i) && albumListView.getItemAtPosition(i) instanceof MusicDirectory.Entry)
|
||||
{
|
||||
someUnselected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selectAll(someUnselected, true);
|
||||
}
|
||||
|
||||
private void selectAll(boolean selected, boolean toast)
|
||||
{
|
||||
int count = albumListView.getCount();
|
||||
int selectedCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(i);
|
||||
if (entry != null && !entry.isDirectory() && !entry.isVideo())
|
||||
{
|
||||
albumListView.setItemChecked(i, selected);
|
||||
selectedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Display toast: N tracks selected / N tracks unselected
|
||||
if (toast)
|
||||
{
|
||||
int toastResId = selected ? R.string.select_album_n_selected : R.string.select_album_n_unselected;
|
||||
Util.toast(getContext(), getString(toastResId, selectedCount));
|
||||
}
|
||||
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
private void enableButtons()
|
||||
{
|
||||
List<MusicDirectory.Entry> selection = getSelectedSongs(albumListView);
|
||||
boolean enabled = !selection.isEmpty();
|
||||
boolean unpinEnabled = false;
|
||||
boolean deleteEnabled = false;
|
||||
|
||||
int pinnedCount = 0;
|
||||
|
||||
for (MusicDirectory.Entry song : selection)
|
||||
{
|
||||
if (song == null) continue;
|
||||
DownloadFile downloadFile = mediaPlayerController.getValue().getDownloadFileForSong(song);
|
||||
if (downloadFile.isWorkDone())
|
||||
{
|
||||
deleteEnabled = true;
|
||||
}
|
||||
|
||||
if (downloadFile.isSaved())
|
||||
{
|
||||
pinnedCount++;
|
||||
unpinEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
playNowButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE);
|
||||
pinButton.setVisibility((enabled && !ActiveServerProvider.Companion.isOffline() && selection.size() > pinnedCount) ? View.VISIBLE : View.GONE);
|
||||
unpinButton.setVisibility(enabled && unpinEnabled ? View.VISIBLE : View.GONE);
|
||||
downloadButton.setVisibility(enabled && !deleteEnabled && !ActiveServerProvider.Companion.isOffline() ? View.VISIBLE : View.GONE);
|
||||
deleteButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void downloadBackground(final boolean save)
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
|
||||
if (songs.isEmpty())
|
||||
{
|
||||
selectAll(true, false);
|
||||
songs = getSelectedSongs(albumListView);
|
||||
}
|
||||
|
||||
downloadBackground(save, songs);
|
||||
}
|
||||
|
||||
private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
|
||||
{
|
||||
Runnable onValid = () -> {
|
||||
networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable();
|
||||
mediaPlayerController.getValue().downloadBackground(songs, save);
|
||||
|
||||
if (save)
|
||||
{
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
|
||||
}
|
||||
};
|
||||
|
||||
onValid.run();
|
||||
}
|
||||
|
||||
private void delete()
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
|
||||
if (songs.isEmpty())
|
||||
{
|
||||
selectAll(true, false);
|
||||
songs = getSelectedSongs(albumListView);
|
||||
}
|
||||
|
||||
mediaPlayerController.getValue().delete(songs);
|
||||
}
|
||||
|
||||
private void unpin()
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size()));
|
||||
mediaPlayerController.getValue().unpin(songs);
|
||||
}
|
||||
|
||||
private abstract class LoadTask extends FragmentBackgroundTask<Pair<MusicDirectory, Boolean>>
|
||||
{
|
||||
public LoadTask()
|
||||
{
|
||||
super(BookmarksFragment.this.getActivity(), true, refreshAlbumListView, cancellationToken);
|
||||
}
|
||||
|
||||
protected abstract MusicDirectory load(MusicService service) throws Exception;
|
||||
|
||||
@Override
|
||||
protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService();
|
||||
MusicDirectory dir = load(musicService);
|
||||
boolean valid = musicService.isLicenseValid();
|
||||
return new Pair<>(dir, valid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Pair<MusicDirectory, Boolean> result)
|
||||
{
|
||||
MusicDirectory musicDirectory = result.getFirst();
|
||||
List<MusicDirectory.Entry> entries = musicDirectory.getChildren();
|
||||
|
||||
int songCount = 0;
|
||||
for (MusicDirectory.Entry entry : entries)
|
||||
{
|
||||
if (!entry.isDirectory())
|
||||
{
|
||||
songCount++;
|
||||
}
|
||||
}
|
||||
|
||||
final int listSize = getArguments() == null? 0 : getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
|
||||
|
||||
if (songCount > 0)
|
||||
{
|
||||
pinButton.setVisibility(View.VISIBLE);
|
||||
unpinButton.setVisibility(View.VISIBLE);
|
||||
downloadButton.setVisibility(View.VISIBLE);
|
||||
deleteButton.setVisibility(View.VISIBLE);
|
||||
playNowButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinButton.setVisibility(View.GONE);
|
||||
unpinButton.setVisibility(View.GONE);
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
deleteButton.setVisibility(View.GONE);
|
||||
playNowButton.setVisibility(View.GONE);
|
||||
|
||||
if (listSize == 0 || result.getFirst().getChildren().size() < listSize)
|
||||
{
|
||||
albumButtons.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
enableButtons();
|
||||
|
||||
emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
|
||||
albumListView.setAdapter(new EntryAdapter(getContext(), imageLoader.getValue().getImageLoader(), entries, true));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,8 +76,8 @@ public class LyricsFragment extends Fragment {
|
|||
{
|
||||
Bundle arguments = getArguments();
|
||||
if (arguments == null) return null;
|
||||
String artist = arguments.getString(Constants.INTENT_EXTRA_NAME_ARTIST);
|
||||
String title = arguments.getString(Constants.INTENT_EXTRA_NAME_TITLE);
|
||||
String artist = arguments.getString(Constants.INTENT_ARTIST);
|
||||
String title = arguments.getString(Constants.INTENT_TITLE);
|
||||
MusicService musicService = MusicServiceFactory.getMusicService();
|
||||
return musicService.getLyrics(artist, title);
|
||||
}
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.PlayerState;
|
||||
import org.moire.ultrasonic.service.DownloadFile;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.NowPlayingEventDistributor;
|
||||
import org.moire.ultrasonic.util.NowPlayingEventListener;
|
||||
import org.moire.ultrasonic.util.Settings;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
|
||||
/**
|
||||
* Contains the mini-now playing information box displayed at the bottom of the screen
|
||||
*/
|
||||
public class NowPlayingFragment extends Fragment {
|
||||
|
||||
private static final int MIN_DISTANCE = 30;
|
||||
private float downX;
|
||||
private float downY;
|
||||
ImageView playButton;
|
||||
ImageView nowPlayingAlbumArtImage;
|
||||
TextView nowPlayingTrack;
|
||||
TextView nowPlayingArtist;
|
||||
|
||||
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
|
||||
private final Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
|
||||
private final Lazy<NowPlayingEventDistributor> nowPlayingEventDistributor = inject(NowPlayingEventDistributor.class);
|
||||
private NowPlayingEventListener nowPlayingEventListener;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.now_playing, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
playButton = view.findViewById(R.id.now_playing_control_play);
|
||||
nowPlayingAlbumArtImage = view.findViewById(R.id.now_playing_image);
|
||||
nowPlayingTrack = view.findViewById(R.id.now_playing_trackname);
|
||||
nowPlayingArtist = view.findViewById(R.id.now_playing_artist);
|
||||
|
||||
nowPlayingEventListener = new NowPlayingEventListener() {
|
||||
@Override
|
||||
public void onDismissNowPlaying() { }
|
||||
@Override
|
||||
public void onHideNowPlaying() { }
|
||||
@Override
|
||||
public void onShowNowPlaying() { update(); }
|
||||
};
|
||||
|
||||
nowPlayingEventDistributor.getValue().subscribe(nowPlayingEventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
nowPlayingEventDistributor.getValue().unsubscribe(nowPlayingEventListener);
|
||||
}
|
||||
|
||||
private void update() {
|
||||
try
|
||||
{
|
||||
PlayerState playerState = mediaPlayerControllerLazy.getValue().getPlayerState();
|
||||
if (playerState == PlayerState.PAUSED) {
|
||||
playButton.setImageDrawable(Util.getDrawableFromAttribute(getContext(), R.attr.media_play));
|
||||
} else if (playerState == PlayerState.STARTED) {
|
||||
playButton.setImageDrawable(Util.getDrawableFromAttribute(getContext(), R.attr.media_pause));
|
||||
}
|
||||
|
||||
DownloadFile file = mediaPlayerControllerLazy.getValue().getCurrentPlaying();
|
||||
if (file != null) {
|
||||
final MusicDirectory.Entry song = file.getSong();
|
||||
String title = song.getTitle();
|
||||
String artist = song.getArtist();
|
||||
|
||||
imageLoader.getValue().getImageLoader().loadImage(nowPlayingAlbumArtImage, song, false, Util.getNotificationImageSize(getContext()));
|
||||
nowPlayingTrack.setText(title);
|
||||
nowPlayingArtist.setText(artist);
|
||||
|
||||
nowPlayingAlbumArtImage.setOnClickListener(v -> {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
if (Settings.getShouldUseId3Tags()) {
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true);
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, song.getAlbumId());
|
||||
} else {
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false);
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, song.getParent());
|
||||
}
|
||||
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, song.getAlbum());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, song.getAlbum());
|
||||
Navigation.findNavController(getActivity(), R.id.nav_host_fragment).navigate(R.id.trackCollectionFragment, bundle);
|
||||
});
|
||||
}
|
||||
|
||||
getView().setOnTouchListener((v, event) -> handleOnTouch(event));
|
||||
|
||||
// This empty onClickListener is necessary for the onTouchListener to work
|
||||
getView().setOnClickListener(v -> {});
|
||||
|
||||
playButton.setOnClickListener(v -> mediaPlayerControllerLazy.getValue().togglePlayPause());
|
||||
}
|
||||
catch (Exception x) {
|
||||
Timber.w(x, "Failed to get notification cover art");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleOnTouch(MotionEvent event) {
|
||||
switch (event.getAction())
|
||||
{
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
{
|
||||
downX = event.getX();
|
||||
downY = event.getY();
|
||||
return false;
|
||||
}
|
||||
case MotionEvent.ACTION_UP:
|
||||
{
|
||||
float upX = event.getX();
|
||||
float upY = event.getY();
|
||||
|
||||
float deltaX = downX - upX;
|
||||
float deltaY = downY - upY;
|
||||
|
||||
if (Math.abs(deltaX) > MIN_DISTANCE)
|
||||
{
|
||||
// left or right
|
||||
if (deltaX < 0)
|
||||
{
|
||||
mediaPlayerControllerLazy.getValue().previous();
|
||||
return false;
|
||||
}
|
||||
if (deltaX > 0)
|
||||
{
|
||||
mediaPlayerControllerLazy.getValue().next();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (Math.abs(deltaY) > MIN_DISTANCE)
|
||||
{
|
||||
if (deltaY < 0)
|
||||
{
|
||||
nowPlayingEventDistributor.getValue().raiseNowPlayingDismissedEvent();
|
||||
return false;
|
||||
}
|
||||
if (deltaY > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Navigation.findNavController(getActivity(), R.id.nav_host_fragment).navigate(R.id.playerFragment);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -102,9 +102,9 @@ public class PlaylistsFragment extends Fragment {
|
|||
}
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
|
||||
bundle.putString(Constants.INTENT_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_PLAYLIST_NAME, playlist.getName());
|
||||
Navigation.findNavController(getView()).navigate(R.id.trackCollectionFragment, bundle);
|
||||
}
|
||||
});
|
||||
|
@ -187,16 +187,16 @@ public class PlaylistsFragment extends Fragment {
|
|||
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, false);
|
||||
} else if (itemId == R.id.playlist_menu_play_now) {
|
||||
bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
|
||||
bundle.putString(Constants.INTENT_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_PLAYLIST_NAME, playlist.getName());
|
||||
bundle.putBoolean(Constants.INTENT_AUTOPLAY, true);
|
||||
Navigation.findNavController(getView()).navigate(R.id.trackCollectionFragment, bundle);
|
||||
} else if (itemId == R.id.playlist_menu_play_shuffled) {
|
||||
bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
|
||||
bundle.putString(Constants.INTENT_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_PLAYLIST_NAME, playlist.getName());
|
||||
bundle.putBoolean(Constants.INTENT_AUTOPLAY, true);
|
||||
bundle.putBoolean(Constants.INTENT_SHUFFLE, true);
|
||||
Navigation.findNavController(getView()).navigate(R.id.trackCollectionFragment, bundle);
|
||||
} else if (itemId == R.id.playlist_menu_delete) {
|
||||
deletePlaylist(playlist);
|
||||
|
@ -212,7 +212,7 @@ public class PlaylistsFragment extends Fragment {
|
|||
|
||||
private void deletePlaylist(final Playlist playlist)
|
||||
{
|
||||
new AlertDialog.Builder(getContext()).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, playlist.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
new AlertDialog.Builder(getContext()).setIcon(R.drawable.ic_baseline_warning).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, playlist.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
|
@ -263,7 +263,7 @@ public class PlaylistsFragment extends Fragment {
|
|||
textView.setText(message);
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
new AlertDialog.Builder(getContext()).setTitle(playlist.getName()).setCancelable(true).setIcon(android.R.drawable.ic_dialog_info).setView(textView).show();
|
||||
new AlertDialog.Builder(getContext()).setTitle(playlist.getName()).setCancelable(true).setIcon(R.drawable.ic_baseline_info).setView(textView).show();
|
||||
}
|
||||
|
||||
private void updatePlaylistInfo(final Playlist playlist)
|
||||
|
@ -294,7 +294,7 @@ public class PlaylistsFragment extends Fragment {
|
|||
|
||||
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
|
||||
|
||||
alertDialog.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
alertDialog.setIcon(R.drawable.ic_baseline_warning);
|
||||
alertDialog.setTitle(R.string.playlist_update_info);
|
||||
alertDialog.setView(dialogView);
|
||||
alertDialog.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
|
|
|
@ -75,7 +75,7 @@ public class PodcastFragment extends Fragment {
|
|||
}
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID, pc.getId());
|
||||
bundle.putString(Constants.INTENT_PODCAST_CHANNEL_ID, pc.getId());
|
||||
Navigation.findNavController(view).navigate(R.id.trackCollectionFragment, bundle);
|
||||
}
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue