Merge branch 'develop' into dependabot/gradle/org.jetbrains.kotlinx-kotlinx-coroutines-android-1.6.0-native-mt

This commit is contained in:
Nite 2022-02-08 19:57:01 +01:00
commit bf96f36cb4
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
43 changed files with 783 additions and 1185 deletions

View File

@ -10,9 +10,9 @@ jobs:
- checkout - checkout
- restore_cache: - restore_cache:
keys: keys:
- v1-ultrasonic-{{ .Branch }}-{{ checksum "dependencies.gradle" }} - v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
- v1-ultrasonic-{{ .Branch }} - v2-ultrasonic-{{ .Branch }}
- v1-ultrasonic - v2-ultrasonic
- run: - run:
name: configure gradle.properties for CI building name: configure gradle.properties for CI building
command: | command: |
@ -44,7 +44,7 @@ jobs:
- save_cache: - save_cache:
paths: paths:
- ~/.gradle - ~/.gradle
key: v1-ultrasonic-{{ .Branch }}-{{ checksum "dependencies.gradle" }} key: v1-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
- store_artifacts: - store_artifacts:
path: ultrasonic/build/reports path: ultrasonic/build/reports
destination: reports destination: reports
@ -81,9 +81,9 @@ jobs:
- checkout - checkout
- restore_cache: - restore_cache:
keys: keys:
- v1-ultrasonic-{{ .Branch }}-{{ checksum "dependencies.gradle" }} - v2-ultrasonic-{{ .Branch }}-{{ checksum "gradle/libs.versions.toml" }}
- v1-ultrasonic-{{ .Branch }} - v2-ultrasonic-{{ .Branch }}
- v1-ultrasonic - v2-ultrasonic
- run: - run:
name: decrypt ultrasonic-keystore 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 command: openssl aes-256-cbc -K ${ULTRASONIC_KEYSTORE_KEY} -iv ${ULTRASONIC_KEYSTORE_IV} -in ultrasonic-keystore.enc -out ultrasonic-keystore -d

View File

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
apply from: 'dependencies.gradle' apply from: 'gradle/versions.gradle'
ext.bootstrap = [ ext.bootstrap = [
kotlinModule : "${project.rootDir}/gradle_scripts/kotlin-module-bootstrap.gradle", kotlinModule : "${project.rootDir}/gradle_scripts/kotlin-module-bootstrap.gradle",
@ -13,11 +13,11 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" } maven { url "https://plugins.gradle.org/m2/" }
} }
dependencies { dependencies {
classpath gradlePlugins.gradle classpath libs.gradle
classpath gradlePlugins.kotlin classpath libs.kotlin
classpath gradlePlugins.ktlintGradle classpath libs.ktlintGradle
classpath gradlePlugins.detekt classpath libs.detekt
classpath gradlePlugins.jacoco classpath libs.jacoco
} }
} }
@ -47,6 +47,6 @@ allprojects {
apply from: 'gradle_scripts/jacoco.gradle' apply from: 'gradle_scripts/jacoco.gradle'
wrapper { wrapper {
gradleVersion(versions.gradle) gradleVersion(libs.versions.gradle.get())
distributionType("all") distributionType("all")
} }

View File

@ -8,7 +8,7 @@ ext {
} }
dependencies { dependencies {
implementation androidSupport.roomRuntime implementation libs.roomRuntime
implementation androidSupport.roomKtx implementation libs.roomKtx
kapt androidSupport.room kapt libs.room
} }

View File

@ -1,24 +1,24 @@
apply from: bootstrap.kotlinModule apply from: bootstrap.kotlinModule
dependencies { dependencies {
api other.retrofit api libs.retrofit
api other.jacksonConverter api libs.jacksonConverter
api other.koinCore api libs.koinCore
implementation(other.jacksonKotlin) { implementation(libs.jacksonKotlin) {
exclude module: 'kotlin-reflect' exclude module: 'kotlin-reflect'
} }
implementation other.kotlinReflect // for jackson kotlin, but to use the same version implementation libs.kotlinReflect // for jackson kotlin, but to use the same version
implementation other.okhttpLogging implementation libs.okhttpLogging
implementation other.timber implementation libs.timber
testImplementation testing.kotlinJunit testImplementation libs.kotlinJunit
testImplementation testing.mockito testImplementation libs.mockito
testImplementation testing.mockitoInline testImplementation libs.mockitoInline
testImplementation testing.mockitoKotlin testImplementation libs.mockitoKotlin
testImplementation testing.kluent testImplementation libs.kluent
testImplementation testing.mockWebServer testImplementation libs.mockWebServer
testImplementation testing.apacheCodecs testImplementation libs.apacheCodecs
} }
ext { ext {

View File

@ -1,8 +1,8 @@
package org.moire.ultrasonic.api.subsonic package org.moire.ultrasonic.api.subsonic
import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockResponse
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should not be` import org.amshove.kluent.`should not be`
import org.junit.Test import org.junit.Test

View File

@ -1,8 +1,8 @@
package org.moire.ultrasonic.api.subsonic package org.moire.ultrasonic.api.subsonic
import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockResponse
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should not be` import org.amshove.kluent.`should not be`
import org.junit.Test import org.junit.Test

View File

@ -1,7 +1,7 @@
package org.moire.ultrasonic.api.subsonic 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`
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should not be` import org.amshove.kluent.`should not be`
import org.junit.Test import org.junit.Test
import org.moire.ultrasonic.api.subsonic.models.MusicDirectory import org.moire.ultrasonic.api.subsonic.models.MusicDirectory

View File

@ -1,8 +1,8 @@
package org.moire.ultrasonic.api.subsonic package org.moire.ultrasonic.api.subsonic
import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockResponse
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should not be` import org.amshove.kluent.`should not be`
import org.junit.Test import org.junit.Test

View File

@ -83,7 +83,7 @@ class SubsonicAPIClient(
// Create the Retrofit instance, and register a special converter factory // 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 // 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/") .baseUrl("${config.baseUrl}/rest/")
.client(okHttpClient) .client(okHttpClient)
.addConverterFactory( .addConverterFactory(
@ -113,13 +113,16 @@ class SubsonicAPIClient(
this.addInterceptor(loggingInterceptor) this.addInterceptor(loggingInterceptor)
} }
@SuppressWarnings("TrustAllX509TrustManager", "EmptyFunctionBlock")
private fun OkHttpClient.Builder.allowSelfSignedCertificates() { private fun OkHttpClient.Builder.allowSelfSignedCertificates() {
val trustManager = object : X509TrustManager { val trustManager =
override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {} @Suppress("CustomX509TrustManager")
override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {} object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray() @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()
}
val sslContext = SSLContext.getInstance("SSL") val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf(trustManager), SecureRandom()) sslContext.init(null, arrayOf(trustManager), SecureRandom())

View File

@ -1,110 +0,0 @@
ext.versions = [
minSdk : 21,
targetSdk : 30,
compileSdk : 30,
// You need to run ./gradlew wrapper after updating the version
gradle : '7.2',
navigation : "2.3.5",
gradlePlugin : "4.2.2",
androidxcore : "1.6.0",
ktlint : "0.37.1",
ktlintGradle : "10.2.0",
detekt : "1.19.0",
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.6.0-native-mt",
viewModelKtx : "2.3.0",
retrofit : "2.6.4",
jackson : "2.9.5",
okhttp : "3.12.13",
koin : "3.0.2",
picasso : "2.71828",
junit4 : "4.13.2",
junit5 : "5.8.1",
mockito : "4.1.0",
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",
]
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",
timber : "com.jakewharton.timber:timber:$versions.timber",
fastScroll : "com.simplecityapps:recyclerview-fastscroll:$versions.fastScroll",
colorPickerView : "com.github.skydoves:colorpickerview:$versions.colorPicker",
rxJava : "io.reactivex.rxjava3:rxjava:$versions.rxJava",
rxAndroid : "io.reactivex.rxjava3:rxandroid:$versions.rxAndroid",
multiType : "com.drakeet.multitype:multitype:$versions.multiType",
]
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"
]

View File

@ -0,0 +1,4 @@
Others
- #671: Bump versions.mockito from 4.1.0 to 4.3.1.
- Update translations.

View File

@ -0,0 +1,4 @@
Otros
- #671: Actualizado versions.mockito de 4.1.0 a 4.3.1.
- Traducciones actualizadas.

100
gradle/libs.versions.toml Normal file
View File

@ -0,0 +1,100 @@
[versions]
# You need to run ./gradlew wrapper after updating the version
gradle = "7.3.2"
navigation = "2.3.5"
gradlePlugin = "7.0.4"
androidxcore = "1.6.0"
ktlint = "0.43.2"
ktlintGradle = "10.2.0"
detekt = "1.19.0"
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.4.0"
kotlin = "1.6.10"
kotlinxCoroutines = "1.6.0-native-mt"
viewModelKtx = "2.3.0"
retrofit = "2.6.4"
jackson = "2.9.5"
okhttp = "3.12.13"
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" }
jacoco = { module = "org.jacoco:org.jacoco.core", version.ref = "jacoco" }
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 = "com.android.support:support-annotations", 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" }
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" }
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" }

5
gradle/versions.gradle Normal file
View File

@ -0,0 +1,5 @@
ext.versions = [
minSdk : 21,
targetSdk : 30,
compileSdk : 31,
]

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -55,14 +55,14 @@ tasks.withType(Test) {
} }
dependencies { dependencies {
api other.kotlinStdlib api libs.kotlinStdlib
testImplementation testing.junit testImplementation libs.junit
testRuntimeOnly testing.junitVintage testRuntimeOnly libs.junitVintage
} }
jacoco { jacoco {
toolVersion(versions.jacoco) toolVersion(libs.versions.jacoco.get())
} }
ext { ext {

View File

@ -6,7 +6,7 @@ if (isCodeQualityEnabled) {
apply plugin: "org.jlleitschuh.gradle.ktlint" apply plugin: "org.jlleitschuh.gradle.ktlint"
ktlint { ktlint {
version = versions.ktlint version = libs.versions.ktlint.get()
outputToConsole = true outputToConsole = true
android = true android = true
} }
@ -21,7 +21,7 @@ if (isCodeQualityEnabled) {
detekt { detekt {
buildUponDefaultConfig = true buildUponDefaultConfig = true
toolVersion = versions.detekt toolVersion = libs.versions.detekt.get()
// Builds the AST in parallel. Rules are always executed in parallel. // Builds the AST in parallel. Rules are always executed in parallel.
// Can lead to speedups in larger projects. // Can lead to speedups in larger projects.
parallel = true parallel = true

View File

@ -1,7 +1,7 @@
apply plugin: 'jacoco' apply plugin: 'jacoco'
jacoco { jacoco {
toolVersion(versions.jacoco) toolVersion(libs.versions.jacoco.get())
} }
def mergedJacocoExec = file("${project.buildDir}/jacoco/jacocoMerged.exec") def mergedJacocoExec = file("${project.buildDir}/jacoco/jacocoMerged.exec")

View File

@ -15,14 +15,14 @@ sourceSets {
dependencies { dependencies {
api other.kotlinStdlib api libs.kotlinStdlib
testImplementation testing.junit testImplementation libs.junit
testRuntimeOnly testing.junitVintage testRuntimeOnly libs.junitVintage
} }
jacoco { jacoco {
toolVersion(versions.jacoco) toolVersion(libs.versions.jacoco.get())
} }
ext { ext {

269
gradlew vendored
View File

@ -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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with 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 # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" 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. # 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"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 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 [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 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." location of your Java installation."
fi fi
else 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. 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 Please set the JAVA_HOME variable in your environment to match the
@ -106,80 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
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" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --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 # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 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" "$@" exec "$JAVACMD" "$@"

View File

@ -1,3 +1,5 @@
enableFeaturePreview("VERSION_CATALOGS")
include ':core:domain' include ':core:domain'
include ':core:subsonic-api' include ':core:subsonic-api'
include ':ultrasonic' include ':ultrasonic'

View File

@ -9,8 +9,8 @@ android {
defaultConfig { defaultConfig {
applicationId "org.moire.ultrasonic" applicationId "org.moire.ultrasonic"
versionCode 99 versionCode 100
versionName "3.0.0" versionName "3.0.1"
minSdkVersion versions.minSdk minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
@ -85,54 +85,54 @@ dependencies {
implementation project(':core:domain') implementation project(':core:domain')
implementation project(':core:subsonic-api') implementation project(':core:subsonic-api')
api(other.picasso) { api(libs.picasso) {
exclude group: "com.android.support" exclude group: "com.android.support"
} }
implementation androidSupport.core implementation libs.core
implementation androidSupport.support implementation libs.support
implementation androidSupport.design implementation libs.design
implementation androidSupport.multidex implementation libs.multidex
implementation androidSupport.roomRuntime implementation libs.roomRuntime
implementation androidSupport.roomKtx implementation libs.roomKtx
implementation androidSupport.viewModelKtx implementation libs.viewModelKtx
implementation androidSupport.constraintLayout implementation libs.constraintLayout
implementation androidSupport.preferences implementation libs.preferences
implementation androidSupport.media implementation libs.media
implementation androidSupport.navigationFragment implementation libs.navigationFragment
implementation androidSupport.navigationUi implementation libs.navigationUi
implementation androidSupport.navigationFragmentKtx implementation libs.navigationFragmentKtx
implementation androidSupport.navigationUiKtx implementation libs.navigationUiKtx
implementation androidSupport.navigationFeature implementation libs.navigationFeature
implementation other.kotlinStdlib implementation libs.kotlinStdlib
implementation other.kotlinxCoroutines implementation libs.kotlinxCoroutines
implementation other.koinAndroid implementation libs.koinAndroid
implementation other.okhttpLogging implementation libs.okhttpLogging
implementation other.fastScroll implementation libs.fastScroll
implementation other.colorPickerView implementation libs.colorPickerView
implementation other.rxJava implementation libs.rxJava
implementation other.rxAndroid implementation libs.rxAndroid
implementation other.multiType implementation libs.multiType
kapt androidSupport.room kapt libs.room
testImplementation other.kotlinReflect testImplementation libs.kotlinReflect
testImplementation testing.junit testImplementation libs.junit
testRuntimeOnly testing.junitVintage testRuntimeOnly libs.junitVintage
testImplementation testing.kotlinJunit testImplementation libs.kotlinJunit
testImplementation testing.kluent testImplementation libs.kluent
testImplementation testing.mockito testImplementation libs.mockito
testImplementation testing.mockitoInline testImplementation libs.mockitoInline
testImplementation testing.mockitoKotlin testImplementation libs.mockitoKotlin
testImplementation testing.robolectric testImplementation libs.robolectric
implementation other.timber implementation libs.timber
} }
jacoco { jacoco {
toolVersion(versions.jacoco) toolVersion(libs.versions.jacoco.get())
} }
// Excluding all java classes and stuff that should not be covered // Excluding all java classes and stuff that should not be covered
@ -156,7 +156,7 @@ ext {
} }
jacoco { jacoco {
toolVersion(versions.jacoco) toolVersion(libs.versions.jacoco.get())
} }
tasks.withType(Test) { tasks.withType(Test) {

View File

@ -1,19 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<issues format="5" by="lint 4.2.2" client="gradle" variant="release" version="4.2.2"> <issues format="6" by="lint 7.0.4" type="baseline" client="gradle" name="AGP (7.0.4)" variant="all" version="7.0.4">
<issue
id="ObsoleteLintCustomCheck"
message="Lint found an issue registry (`androidx.appcompat.AppCompatIssueRegistry`) which is older than the current API level; these checks may not work correctly.&#xA;&#xA;Recompile the checks against the latest version. Custom check API version is 7 (4.0), current lint API level is 8 (4.1)">
<location
file="../../../../.gradle/caches/transforms-3/cfd0dffad81bb5ff9173a2c6d47f5e0a/transformed/appcompat-1.2.0/jars/lint.jar"/>
</issue>
<issue
id="ObsoleteLintCustomCheck"
message="Lint found an issue registry (`timber.lint.TimberIssueRegistry`) which is older than the current API level; these checks may not work correctly.&#xA;&#xA;Recompile the checks against the latest version. Custom check API version is 1 (3.1), current lint API level is 8 (4.1)">
<location
file="../../../../.gradle/caches/transforms-3/9362f92689454001c7932b460b41bbdb/transformed/jetified-timber-4.7.1/jars/lint.jar"/>
</issue>
<issue <issue
id="InflateParams" id="InflateParams"
@ -81,6 +67,20 @@
column="9"/> column="9"/>
</issue> </issue>
<issue
id="TrustAllX509TrustManager"
message="`checkClientTrusted` is empty, which could cause insecure network traffic due to trusting arbitrary TLS/SSL certificates presented by peers">
<location
file="org/moire/ultrasonic/api/subsonic/SubsonicAPIClient$allowSelfSignedCertificates$trustManager$1.class"/>
</issue>
<issue
id="TrustAllX509TrustManager"
message="`checkServerTrusted` is empty, which could cause insecure network traffic due to trusting arbitrary TLS/SSL certificates presented by peers">
<location
file="org/moire/ultrasonic/api/subsonic/SubsonicAPIClient$allowSelfSignedCertificates$trustManager$1.class"/>
</issue>
<issue <issue
id="ExportedContentProvider" id="ExportedContentProvider"
message="Exported content providers can provide access to potentially sensitive data" message="Exported content providers can provide access to potentially sensitive data"
@ -103,6 +103,182 @@
column="10"/> column="10"/>
</issue> </issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise. For launcher activities, this should be set to `true`."
errorLine1=" &lt;activity android:name=&quot;.activity.NavigationActivity&quot;"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="41"
column="10"/>
</issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise."
errorLine1=" &lt;receiver android:name=&quot;.receiver.MediaButtonIntentReceiver&quot;>"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="76"
column="10"/>
</issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise."
errorLine1=" &lt;receiver android:name=&quot;.receiver.UltrasonicIntentReceiver&quot;>"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="81"
column="10"/>
</issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise."
errorLine1=" &lt;receiver android:name=&quot;.receiver.BluetoothIntentReceiver&quot;>"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="93"
column="10"/>
</issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise."
errorLine1=" &lt;receiver"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="101"
column="10"/>
</issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise."
errorLine1=" &lt;receiver"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="112"
column="10"/>
</issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise."
errorLine1=" &lt;receiver"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="123"
column="10"/>
</issue>
<issue
id="IntentFilterExportedReceiver"
message="As of Android 12, `android:exported` must be set; use `true` to make the activity \&#xA;available to other apps, and `false` otherwise."
errorLine1=" &lt;receiver"
errorLine2=" ~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="134"
column="10"/>
</issue>
<issue
id="UnspecifiedImmutableFlag"
message="Missing `PendingIntent` mutability flag"
errorLine1=" return PendingIntent.getActivity(this, 0, intent, flags)"
errorLine2=" ~~~~~">
<location
file="src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt"
line="708"
column="59"/>
</issue>
<issue
id="UnspecifiedImmutableFlag"
message="Missing `PendingIntent` mutability flag"
errorLine1=" PendingIntent.FLAG_CANCEL_CURRENT"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/org/moire/ultrasonic/util/MediaSessionHandler.kt"
line="323"
column="13"/>
</issue>
<issue
id="UnspecifiedImmutableFlag"
message="Missing `PendingIntent` mutability flag"
errorLine1=" PendingIntent pendingIntent = PendingIntent.getActivity(context, 10, intent, PendingIntent.FLAG_UPDATE_CURRENT);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java"
line="198"
column="80"/>
</issue>
<issue
id="UnspecifiedImmutableFlag"
message="Missing `PendingIntent` mutability flag"
errorLine1=" pendingIntent = PendingIntent.getBroadcast(context, 11, intent, 0);"
errorLine2=" ~">
<location
file="src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java"
line="206"
column="67"/>
</issue>
<issue
id="UnspecifiedImmutableFlag"
message="Missing `PendingIntent` mutability flag"
errorLine1=" pendingIntent = PendingIntent.getBroadcast(context, 12, intent, 0);"
errorLine2=" ~">
<location
file="src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java"
line="212"
column="67"/>
</issue>
<issue
id="UnspecifiedImmutableFlag"
message="Missing `PendingIntent` mutability flag"
errorLine1=" pendingIntent = PendingIntent.getBroadcast(context, 13, intent, 0);"
errorLine2=" ~">
<location
file="src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java"
line="218"
column="67"/>
</issue>
<issue
id="UnspecifiedImmutableFlag"
message="Missing `PendingIntent` mutability flag"
errorLine1=" return PendingIntent.getBroadcast(context, requestCode, intent, flags)"
errorLine2=" ~~~~~">
<location
file="src/main/kotlin/org/moire/ultrasonic/util/Util.kt"
line="891"
column="73"/>
</issue>
<issue
id="NotifyDataSetChanged"
message="It will always be more efficient to use more specific change events if you can. Rely on `notifyDataSetChanged` as a last resort."
errorLine1=" viewAdapter.notifyDataSetChanged()"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt"
line="908"
column="21"/>
</issue>
<issue <issue
id="ObsoleteLayoutParam" id="ObsoleteLayoutParam"
message="Invalid layout param in a `LinearLayout`: `layout_above`" message="Invalid layout param in a `LinearLayout`: `layout_above`"
@ -200,54 +376,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="114" line="114"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="98"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="97"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="111"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="108"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="108"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="95"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="111"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="97"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="97"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="108"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="108"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="107"
column="13"/>
</issue> </issue>
<issue <issue
@ -259,54 +387,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="128" line="128"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="109"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="108"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="124"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="121"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="119"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="106"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="124"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="108"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="108"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="121"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="121"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="120"
column="13"/>
</issue> </issue>
<issue <issue
@ -318,54 +398,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="133" line="133"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="114"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="113"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="129"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="126"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="124"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="110"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="129"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="112"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="112"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="126"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="126"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="125"
column="13"/>
</issue> </issue>
<issue <issue
@ -377,54 +409,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="134" line="134"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="115"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="114"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="130"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="127"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="125"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="111"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="130"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="113"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="113"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="127"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="127"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="126"
column="13"/>
</issue> </issue>
<issue <issue
@ -436,54 +420,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="135" line="135"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="116"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="115"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="131"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="128"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="126"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="112"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="131"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="114"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="114"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="128"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="128"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="127"
column="13"/>
</issue> </issue>
<issue <issue
@ -495,54 +431,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="136" line="136"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="117"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="116"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="132"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="129"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="127"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="113"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="132"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="115"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="115"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="129"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="129"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="128"
column="13"/>
</issue> </issue>
<issue <issue
@ -554,54 +442,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="141" line="141"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="122"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="121"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="137"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="134"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="132"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="118"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="137"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="120"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="120"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="134"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="134"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="133"
column="13"/>
</issue> </issue>
<issue <issue
@ -613,54 +453,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="147" line="147"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="128"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="127"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="143"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="140"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="138"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="124"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="143"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="126"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="126"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="140"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="140"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="139"
column="13"/>
</issue> </issue>
<issue <issue
@ -672,54 +464,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="159" line="159"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="140"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="139"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="155"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="152"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="150"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="136"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="155"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="138"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="138"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="152"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="152"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="151"
column="13"/>
</issue> </issue>
<issue <issue
@ -753,54 +497,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="230" line="230"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="208"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="207"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="223"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="220"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="218"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="203"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="223"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="206"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="206"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="220"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="220"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="217"
column="13"/>
</issue> </issue>
<issue <issue
@ -812,54 +508,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="299" line="299"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="273"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="272"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="292"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="287"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="285"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="266"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="292"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="271"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="271"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="289"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="287"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="285"
column="13"/>
</issue> </issue>
<issue <issue
@ -871,54 +519,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="302" line="302"
column="13"/> column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="276"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="275"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="295"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="290"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="288"
column="13"/>
<location
file="src/main/res/values-it/strings.xml"
line="269"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="295"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="274"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="274"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="292"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="290"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="288"
column="13"/>
</issue> </issue>
<issue <issue
@ -941,46 +541,6 @@
file="src/main/res/values/strings.xml" file="src/main/res/values/strings.xml"
line="447" line="447"
column="14"/> column="14"/>
<location
file="src/main/res/values-cs/strings.xml"
line="413"
column="14"/>
<location
file="src/main/res/values-es/strings.xml"
line="436"
column="14"/>
<location
file="src/main/res/values-fr/strings.xml"
line="425"
column="14"/>
<location
file="src/main/res/values-hu/strings.xml"
line="420"
column="14"/>
<location
file="src/main/res/values-nl/strings.xml"
line="436"
column="14"/>
<location
file="src/main/res/values-pl/strings.xml"
line="388"
column="14"/>
<location
file="src/main/res/values-pt/strings.xml"
line="375"
column="14"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="429"
column="14"/>
<location
file="src/main/res/values-ru/strings.xml"
line="437"
column="14"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="419"
column="14"/>
</issue> </issue>
<issue <issue

View File

@ -18,22 +18,24 @@
*/ */
package org.moire.ultrasonic.receiver; package org.moire.ultrasonic.receiver;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import timber.log.Timber;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.Settings; import org.moire.ultrasonic.util.Settings;
import org.moire.ultrasonic.util.Util;
import timber.log.Timber;
/** /**
* Resume or pause playback on Bluetooth A2DP connect/disconnect. * Resume or pause playback on Bluetooth A2DP connect/disconnect.
* *
* @author Sindre Mehus * @author Sindre Mehus
*/ */
@SuppressLint("MissingPermission")
public class BluetoothIntentReceiver extends BroadcastReceiver public class BluetoothIntentReceiver extends BroadcastReceiver
{ {
@Override @Override

View File

@ -26,6 +26,8 @@
*/ */
package org.moire.ultrasonic.service.ssl; package org.moire.ultrasonic.service.ssl;
import android.annotation.SuppressLint;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
@ -34,6 +36,7 @@ import javax.net.ssl.X509TrustManager;
/** /**
* @since 4.1 * @since 4.1
*/ */
@SuppressLint("CustomX509TrustManager")
class TrustManagerDecorator implements X509TrustManager class TrustManagerDecorator implements X509TrustManager
{ {

View File

@ -1,117 +0,0 @@
/*
This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus
*/
package org.moire.ultrasonic.util;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
/**
* @author Sindre Mehus
*/
public class LRUCache<K, V>
{
private final int capacity;
private final Map<K, TimestampedValue> map;
public LRUCache(int capacity)
{
map = new HashMap<K, TimestampedValue>(capacity);
this.capacity = capacity;
}
public synchronized V get(K key)
{
TimestampedValue value = map.get(key);
V result = null;
if (value != null)
{
value.updateTimestamp();
result = value.getValue();
}
return result;
}
public synchronized void put(K key, V value)
{
if (map.size() >= capacity)
{
removeOldest();
}
map.put(key, new TimestampedValue(value));
}
public void clear()
{
map.clear();
}
private void removeOldest()
{
K oldestKey = null;
long oldestTimestamp = Long.MAX_VALUE;
for (Map.Entry<K, TimestampedValue> entry : map.entrySet())
{
K key = entry.getKey();
TimestampedValue value = entry.getValue();
if (value.getTimestamp() < oldestTimestamp)
{
oldestTimestamp = value.getTimestamp();
oldestKey = key;
}
}
if (oldestKey != null)
{
map.remove(oldestKey);
}
}
private final class TimestampedValue
{
private final SoftReference<V> value;
private long timestamp;
public TimestampedValue(V value)
{
this.value = new SoftReference<V>(value);
updateTimestamp();
}
public V getValue()
{
return value.get();
}
public long getTimestamp()
{
return timestamp;
}
public void updateTimestamp()
{
timestamp = System.currentTimeMillis();
}
}
}

View File

@ -208,8 +208,8 @@ class NavigationActivity : AppCompatActivity() {
selectServerButton?.text = getString(R.string.main_setup_server, activeServer.name) selectServerButton?.text = getString(R.string.main_setup_server, activeServer.name)
else selectServerButton?.text = activeServer.name else selectServerButton?.text = activeServer.name
val foregroundColor = ServerColor.getForegroundColor(this, null) val foregroundColor = ServerColor.getForegroundColor(this, activeServer.color)
val backgroundColor = ServerColor.getBackgroundColor(this, null) val backgroundColor = ServerColor.getBackgroundColor(this, activeServer.color)
if (activeServer.index == 0) if (activeServer.index == 0)
selectServerButton?.icon = selectServerButton?.icon =

View File

@ -108,8 +108,8 @@ internal class ServerRowAdapter(
} }
// Set colors // Set colors
icon?.setTint(ServerColor.getForegroundColor(context, null)) icon?.setTint(ServerColor.getForegroundColor(context, setting?.color))
background?.setTint(ServerColor.getBackgroundColor(context, null)) background?.setTint(ServerColor.getBackgroundColor(context, setting?.color))
// Set the final drawables // Set the final drawables
image?.setImageDrawable(icon) image?.setImageDrawable(icon)

View File

@ -38,7 +38,10 @@ class UApp : MultiDexApplication() {
} }
startKoin { startKoin {
logger(TimberKoinLogger(Level.INFO)) // TODO Currently there is a bug in Koin which makes necessary to set the loglevel to ERROR
logger(TimberKoinLogger(Level.ERROR))
// logger(TimberKoinLogger(Level.INFO))
// declare Android context // declare Android context
androidContext(this@UApp) androidContext(this@UApp)
// declare modules to use // declare modules to use

View File

@ -9,7 +9,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
* Room Database to be used to store global data for the whole app. * Room Database to be used to store global data for the whole app.
* This could be settings or data that are not specific to any remote music database * This could be settings or data that are not specific to any remote music database
*/ */
@Database(entities = [ServerSetting::class], version = 3) @Database(entities = [ServerSetting::class], version = 4)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
/** /**

View File

@ -23,6 +23,7 @@ data class ServerSetting(
@ColumnInfo(name = "index") var index: Int, @ColumnInfo(name = "index") var index: Int,
@ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "name") var name: String,
@ColumnInfo(name = "url") var url: String, @ColumnInfo(name = "url") var url: String,
@ColumnInfo(name = "color") var color: Int? = null,
@ColumnInfo(name = "userName") var userName: String, @ColumnInfo(name = "userName") var userName: String,
@ColumnInfo(name = "password") var password: String, @ColumnInfo(name = "password") var password: String,
@ColumnInfo(name = "jukeboxByDefault") var jukeboxByDefault: Boolean, @ColumnInfo(name = "jukeboxByDefault") var jukeboxByDefault: Boolean,
@ -36,9 +37,9 @@ data class ServerSetting(
@ColumnInfo(name = "podcastSupport") var podcastSupport: Boolean? = null @ColumnInfo(name = "podcastSupport") var podcastSupport: Boolean? = null
) { ) {
constructor() : this ( constructor() : this (
-1, 0, "", "", "", "", false, false, false, null, null -1, 0, "", "", null, "", "", false, false, false, null, null
) )
constructor(name: String, url: String) : this( constructor(name: String, url: String) : this(
-1, 0, name, url, "", "", false, false, false, null, null -1, 0, name, url, null, "", "", false, false, false, null, null
) )
} }

View File

@ -6,13 +6,15 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.switchmaterial.SwitchMaterial import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.skydoves.colorpickerview.ColorPickerDialog
import com.skydoves.colorpickerview.flag.BubbleFlag
import com.skydoves.colorpickerview.flag.FlagMode
import com.skydoves.colorpickerview.listeners.ColorEnvelopeListener
import java.io.IOException import java.io.IOException
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
@ -41,6 +43,8 @@ import org.moire.ultrasonic.util.Util
import retrofit2.Response import retrofit2.Response
import timber.log.Timber import timber.log.Timber
private const val DIALOG_PADDING = 12
/** /**
* Displays a form where server settings can be created / edited * Displays a form where server settings can be created / edited
*/ */
@ -68,8 +72,6 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
private var currentColor: Int = 0 private var currentColor: Int = 0
private var selectedColor: Int? = null private var selectedColor: Int? = null
private var editServerColorText: TextView? = null
@Override @Override
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Util.applyTheme(this.context) Util.applyTheme(this.context)
@ -97,7 +99,6 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
jukeboxSwitch = view.findViewById(R.id.edit_jukebox) jukeboxSwitch = view.findViewById(R.id.edit_jukebox)
saveButton = view.findViewById(R.id.edit_save) saveButton = view.findViewById(R.id.edit_save)
testButton = view.findViewById(R.id.edit_test) testButton = view.findViewById(R.id.edit_test)
editServerColorText = view.findViewById(R.id.edit_server_color_text)
val index = arguments?.getInt( val index = arguments?.getInt(
EDIT_SERVER_INTENT_INDEX, EDIT_SERVER_INTENT_INDEX,
@ -146,7 +147,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
} else { } else {
// Creating a new server // Creating a new server
FragmentTitle.setTitle(this, R.string.server_editor_new_label) FragmentTitle.setTitle(this, R.string.server_editor_new_label)
// updateColor(null) updateColor(null)
currentServerSetting = ServerSetting() currentServerSetting = ServerSetting()
saveButton!!.setOnClickListener { saveButton!!.setOnClickListener {
if (getFields()) { if (getFields()) {
@ -162,37 +163,33 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
} }
} }
// serverColorImageView!!.setOnClickListener { serverColorImageView!!.setOnClickListener {
// val bubbleFlag = BubbleFlag(context) val bubbleFlag = BubbleFlag(context)
// bubbleFlag.flagMode = FlagMode.LAST bubbleFlag.flagMode = FlagMode.LAST
// ColorPickerDialog.Builder(context).apply { ColorPickerDialog.Builder(context).apply {
// this.colorPickerView.setInitialColor(currentColor) this.colorPickerView.setInitialColor(currentColor)
// this.colorPickerView.flagView = bubbleFlag this.colorPickerView.flagView = bubbleFlag
// } }
// .attachAlphaSlideBar(false) .attachAlphaSlideBar(false)
// .setPositiveButton( .setPositiveButton(
// getString(R.string.common_ok), getString(R.string.common_ok),
// ColorEnvelopeListener { envelope, _ -> ColorEnvelopeListener { envelope, _ ->
// selectedColor = envelope.color selectedColor = envelope.color
// updateColor(envelope.color) updateColor(envelope.color)
// } }
// ) )
// .setNegativeButton(getString(R.string.common_cancel)) { .setNegativeButton(getString(R.string.common_cancel)) {
// dialogInterface, _ -> dialogInterface, _ ->
// dialogInterface.dismiss() dialogInterface.dismiss()
// } }
// .setBottomSpace(DIALOG_PADDING) .setBottomSpace(DIALOG_PADDING)
// .show() .show()
// } }
serverColorImageView?.isVisible = false
editServerColorText?.isVisible = false
} }
@Suppress("unused") private fun updateColor(color: Int?) {
private fun updateColor() {
val image = ContextCompat.getDrawable(requireContext(), R.drawable.thumb_drawable) val image = ContextCompat.getDrawable(requireContext(), R.drawable.thumb_drawable)
currentColor = ServerColor.getBackgroundColor(requireContext(), null) currentColor = ServerColor.getBackgroundColor(requireContext(), color)
image?.setTint(currentColor) image?.setTint(currentColor)
serverColorImageView?.background = image serverColorImageView?.background = image
} }
@ -257,7 +254,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
selfSignedSwitch!!.isChecked = savedInstanceState.getBoolean(::selfSignedSwitch.name) selfSignedSwitch!!.isChecked = savedInstanceState.getBoolean(::selfSignedSwitch.name)
ldapSwitch!!.isChecked = savedInstanceState.getBoolean(::ldapSwitch.name) ldapSwitch!!.isChecked = savedInstanceState.getBoolean(::ldapSwitch.name)
jukeboxSwitch!!.isChecked = savedInstanceState.getBoolean(::jukeboxSwitch.name) jukeboxSwitch!!.isChecked = savedInstanceState.getBoolean(::jukeboxSwitch.name)
// updateColor(savedInstanceState.getInt(::serverColorImageView.name)) updateColor(savedInstanceState.getInt(::serverColorImageView.name))
if (savedInstanceState.containsKey(::selectedColor.name)) if (savedInstanceState.containsKey(::selectedColor.name))
selectedColor = savedInstanceState.getInt(::selectedColor.name) selectedColor = savedInstanceState.getInt(::selectedColor.name)
isInstanceStateSaved = savedInstanceState.getBoolean(::isInstanceStateSaved.name) isInstanceStateSaved = savedInstanceState.getBoolean(::isInstanceStateSaved.name)
@ -276,7 +273,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
selfSignedSwitch!!.isChecked = currentServerSetting!!.allowSelfSignedCertificate selfSignedSwitch!!.isChecked = currentServerSetting!!.allowSelfSignedCertificate
ldapSwitch!!.isChecked = currentServerSetting!!.ldapSupport ldapSwitch!!.isChecked = currentServerSetting!!.ldapSupport
jukeboxSwitch!!.isChecked = currentServerSetting!!.jukeboxByDefault jukeboxSwitch!!.isChecked = currentServerSetting!!.jukeboxByDefault
// updateColor(currentServerSetting!!.color) updateColor(currentServerSetting!!.color)
} }
/** /**
@ -325,7 +322,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
if (isValid) { if (isValid) {
currentServerSetting!!.name = serverNameEditText!!.editText?.text.toString() currentServerSetting!!.name = serverNameEditText!!.editText?.text.toString()
currentServerSetting!!.url = serverAddressEditText!!.editText?.text.toString() currentServerSetting!!.url = serverAddressEditText!!.editText?.text.toString()
// currentServerSetting!!.color = selectedColor currentServerSetting!!.color = selectedColor
currentServerSetting!!.userName = userNameEditText!!.editText?.text.toString() currentServerSetting!!.userName = userNameEditText!!.editText?.text.toString()
currentServerSetting!!.password = passwordEditText!!.editText?.text.toString() currentServerSetting!!.password = passwordEditText!!.editText?.text.toString()
currentServerSetting!!.allowSelfSignedCertificate = selfSignedSwitch!!.isChecked currentServerSetting!!.allowSelfSignedCertificate = selfSignedSwitch!!.isChecked
@ -459,11 +456,11 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
override fun done(responseString: String) { override fun done(responseString: String) {
var dialogText = responseString var dialogText = responseString
if (arrayOf( if (arrayOf(
currentServerSetting!!.chatSupport, currentServerSetting!!.chatSupport,
currentServerSetting!!.bookmarkSupport, currentServerSetting!!.bookmarkSupport,
currentServerSetting!!.shareSupport, currentServerSetting!!.shareSupport,
currentServerSetting!!.podcastSupport currentServerSetting!!.podcastSupport
).any { x -> x == false } ).any { x -> x == false }
) { ) {
dialogText = String.format( dialogText = String.format(
Locale.ROOT, Locale.ROOT,

View File

@ -338,9 +338,9 @@ class PlayerFragment :
registerForContextMenu(playlistView) registerForContextMenu(playlistView)
if (arguments != null && requireArguments().getBoolean( if (arguments != null && requireArguments().getBoolean(
Constants.INTENT_SHUFFLE, Constants.INTENT_SHUFFLE,
false false
) )
) { ) {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
mediaPlayerController.isShufflePlayEnabled = true mediaPlayerController.isShufflePlayEnabled = true

View File

@ -212,6 +212,7 @@ class ServerSettingsModel(
serverId, serverId,
settings.getString(PREFERENCES_KEY_SERVER_NAME + preferenceId, "")!!, settings.getString(PREFERENCES_KEY_SERVER_NAME + preferenceId, "")!!,
url, url,
null,
userName, userName,
settings.getString(PREFERENCES_KEY_PASSWORD + preferenceId, "")!!, settings.getString(PREFERENCES_KEY_PASSWORD + preferenceId, "")!!,
settings.getBoolean(PREFERENCES_KEY_JUKEBOX_BY_DEFAULT + preferenceId, false), settings.getBoolean(PREFERENCES_KEY_JUKEBOX_BY_DEFAULT + preferenceId, false),

View File

@ -150,19 +150,19 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
@Throws(Exception::class) @Throws(Exception::class)
override fun getArtist(id: String, name: String?, refresh: Boolean): override fun getArtist(id: String, name: String?, refresh: Boolean):
List<MusicDirectory.Album> { List<MusicDirectory.Album> {
checkSettingsChanged() checkSettingsChanged()
var cache = if (refresh) null else cachedArtist[id] var cache = if (refresh) null else cachedArtist[id]
var dir = cache?.get() var dir = cache?.get()
if (dir == null) { if (dir == null) {
dir = musicService.getArtist(id, name, refresh) dir = musicService.getArtist(id, name, refresh)
cache = TimeLimitedCache( cache = TimeLimitedCache(
Settings.directoryCacheTime.toLong(), TimeUnit.SECONDS Settings.directoryCacheTime.toLong(), TimeUnit.SECONDS
) )
cache.set(dir) cache.set(dir)
cachedArtist.put(id, cache) cachedArtist.put(id, cache)
}
return dir
} }
return dir
}
@Throws(Exception::class) @Throws(Exception::class)
override fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory { override fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory {

View File

@ -149,9 +149,9 @@ class MediaPlayerLifecycleSupport : KoinComponent {
} else if (state == 1) { } else if (state == 1) {
if (!mediaPlayerController.isJukeboxEnabled && if (!mediaPlayerController.isJukeboxEnabled &&
sp.getBoolean( sp.getBoolean(
spKey, spKey,
false false
) && mediaPlayerController.playerState === PlayerState.PAUSED ) && mediaPlayerController.playerState === PlayerState.PAUSED
) { ) {
mediaPlayerController.start() mediaPlayerController.start()
} }

View File

@ -456,8 +456,8 @@ class OfflineMusicService : MusicService, KoinComponent {
@Throws(OfflineException::class) @Throws(OfflineException::class)
override fun getArtist(id: String, name: String?, refresh: Boolean): override fun getArtist(id: String, name: String?, refresh: Boolean):
List<MusicDirectory.Album> { List<MusicDirectory.Album> {
throw OfflineException("getArtist isn't available in offline mode") throw OfflineException("getArtist isn't available in offline mode")
} }
@Throws(OfflineException::class) @Throws(OfflineException::class)
override fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory { override fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory {

View File

@ -30,13 +30,13 @@ import timber.log.Timber
object CommunicationError { object CommunicationError {
fun getHandler(context: Context?, handler: ((CoroutineContext, Throwable) -> Unit)? = null): fun getHandler(context: Context?, handler: ((CoroutineContext, Throwable) -> Unit)? = null):
CoroutineExceptionHandler { CoroutineExceptionHandler {
return CoroutineExceptionHandler { coroutineContext, exception -> return CoroutineExceptionHandler { coroutineContext, exception ->
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
handleError(exception, context) handleError(exception, context)
handler?.invoke(coroutineContext, exception) handler?.invoke(coroutineContext, exception)
}
} }
} }
}
@JvmStatic @JvmStatic
fun handleError(error: Throwable?, context: Context?) { fun handleError(error: Throwable?, context: Context?) {

View File

@ -0,0 +1,79 @@
/*
* LRUCache.kt
* Copyright (C) 2009-2021 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.util
import java.lang.ref.SoftReference
import java.util.HashMap
/**
* A cache that deletes the least-recently-used items.
*/
class LRUCache<K, V>(capacity: Int) {
private val capacity: Int
private val map: MutableMap<K, TimestampedValue>
@Synchronized
operator fun get(key: K): V? {
val value = map[key]
var result: V? = null
if (value != null) {
value.updateTimestamp()
result = value.getValue()
}
return result
}
@Synchronized
fun put(key: K, value: V) {
if (map.size >= capacity) {
removeOldest()
}
map[key] = TimestampedValue(value)
}
fun clear() {
map.clear()
}
private fun removeOldest() {
var oldestKey: K? = null
var oldestTimestamp = Long.MAX_VALUE
for ((key, value) in map) {
if (value.timestamp < oldestTimestamp) {
oldestTimestamp = value.timestamp
oldestKey = key
}
}
if (oldestKey != null) {
map.remove(oldestKey)
}
}
private inner class TimestampedValue(value: V) {
private val value: SoftReference<V> = SoftReference(value)
var timestamp: Long = 0
private set
fun getValue(): V? {
return value.get()
}
fun updateTimestamp() {
timestamp = System.currentTimeMillis()
}
init {
updateTimestamp()
}
}
init {
map = HashMap(capacity)
this.capacity = capacity
}
}

View File

@ -133,55 +133,55 @@ object Settings {
@JvmStatic @JvmStatic
var shouldUseFolderForArtistName var shouldUseFolderForArtistName
by BooleanSetting(Constants.PREFERENCES_KEY_USE_FOLDER_FOR_ALBUM_ARTIST, false) by BooleanSetting(Constants.PREFERENCES_KEY_USE_FOLDER_FOR_ALBUM_ARTIST, false)
@JvmStatic @JvmStatic
var shouldShowTrackNumber var shouldShowTrackNumber
by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_TRACK_NUMBER, false) by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_TRACK_NUMBER, false)
@JvmStatic @JvmStatic
var defaultAlbums var defaultAlbums
by StringIntSetting(Constants.PREFERENCES_KEY_DEFAULT_ALBUMS, "5") by StringIntSetting(Constants.PREFERENCES_KEY_DEFAULT_ALBUMS, "5")
@JvmStatic @JvmStatic
var maxAlbums var maxAlbums
by StringIntSetting(Constants.PREFERENCES_KEY_MAX_ALBUMS, "20") by StringIntSetting(Constants.PREFERENCES_KEY_MAX_ALBUMS, "20")
@JvmStatic @JvmStatic
var defaultSongs var defaultSongs
by StringIntSetting(Constants.PREFERENCES_KEY_DEFAULT_SONGS, "10") by StringIntSetting(Constants.PREFERENCES_KEY_DEFAULT_SONGS, "10")
@JvmStatic @JvmStatic
var maxSongs var maxSongs
by StringIntSetting(Constants.PREFERENCES_KEY_MAX_SONGS, "25") by StringIntSetting(Constants.PREFERENCES_KEY_MAX_SONGS, "25")
@JvmStatic @JvmStatic
var maxArtists var maxArtists
by StringIntSetting(Constants.PREFERENCES_KEY_MAX_ARTISTS, "10") by StringIntSetting(Constants.PREFERENCES_KEY_MAX_ARTISTS, "10")
@JvmStatic @JvmStatic
var defaultArtists var defaultArtists
by StringIntSetting(Constants.PREFERENCES_KEY_DEFAULT_ARTISTS, "3") by StringIntSetting(Constants.PREFERENCES_KEY_DEFAULT_ARTISTS, "3")
@JvmStatic @JvmStatic
var bufferLength var bufferLength
by StringIntSetting(Constants.PREFERENCES_KEY_BUFFER_LENGTH, "5") by StringIntSetting(Constants.PREFERENCES_KEY_BUFFER_LENGTH, "5")
@JvmStatic @JvmStatic
var incrementTime var incrementTime
by StringIntSetting(Constants.PREFERENCES_KEY_INCREMENT_TIME, "5") by StringIntSetting(Constants.PREFERENCES_KEY_INCREMENT_TIME, "5")
@JvmStatic @JvmStatic
var mediaButtonsEnabled var mediaButtonsEnabled
by BooleanSetting(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true) by BooleanSetting(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true)
@JvmStatic @JvmStatic
var showNowPlaying var showNowPlaying
by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_NOW_PLAYING, true) by BooleanSetting(Constants.PREFERENCES_KEY_SHOW_NOW_PLAYING, true)
@JvmStatic @JvmStatic
var gaplessPlayback var gaplessPlayback
by BooleanSetting(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK, false) by BooleanSetting(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK, false)
@JvmStatic @JvmStatic
var shouldTransitionOnPlayback by BooleanSetting( var shouldTransitionOnPlayback by BooleanSetting(
@ -191,7 +191,7 @@ object Settings {
@JvmStatic @JvmStatic
var shouldUseId3Tags var shouldUseId3Tags
by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false) by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
@JvmStatic @JvmStatic
var tempLoss by StringIntSetting(Constants.PREFERENCES_KEY_TEMP_LOSS, "1") var tempLoss by StringIntSetting(Constants.PREFERENCES_KEY_TEMP_LOSS, "1")
@ -224,19 +224,19 @@ object Settings {
) )
var shouldClearPlaylist var shouldClearPlaylist
by BooleanSetting(Constants.PREFERENCES_KEY_CLEAR_PLAYLIST, false) by BooleanSetting(Constants.PREFERENCES_KEY_CLEAR_PLAYLIST, false)
var shouldSortByDisc var shouldSortByDisc
by BooleanSetting(Constants.PREFERENCES_KEY_DISC_SORT, false) by BooleanSetting(Constants.PREFERENCES_KEY_DISC_SORT, false)
var shouldClearBookmark var shouldClearBookmark
by BooleanSetting(Constants.PREFERENCES_KEY_CLEAR_BOOKMARK, false) by BooleanSetting(Constants.PREFERENCES_KEY_CLEAR_BOOKMARK, false)
var singleButtonPlayPause var singleButtonPlayPause
by BooleanSetting( by BooleanSetting(
Constants.PREFERENCES_KEY_SINGLE_BUTTON_PLAY_PAUSE, Constants.PREFERENCES_KEY_SINGLE_BUTTON_PLAY_PAUSE,
false false
) )
// Inverted for readability // Inverted for readability
var shouldSendBluetoothNotifications by BooleanSetting( var shouldSendBluetoothNotifications by BooleanSetting(
@ -245,20 +245,20 @@ object Settings {
) )
var shouldSendBluetoothAlbumArt var shouldSendBluetoothAlbumArt
by BooleanSetting(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART, true) by BooleanSetting(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART, true)
var shouldDisableNowPlayingListSending var shouldDisableNowPlayingListSending
by BooleanSetting(Constants.PREFERENCES_KEY_DISABLE_SEND_NOW_PLAYING_LIST, false) by BooleanSetting(Constants.PREFERENCES_KEY_DISABLE_SEND_NOW_PLAYING_LIST, false)
@JvmStatic @JvmStatic
var viewRefreshInterval var viewRefreshInterval
by StringIntSetting(Constants.PREFERENCES_KEY_VIEW_REFRESH, "1000") by StringIntSetting(Constants.PREFERENCES_KEY_VIEW_REFRESH, "1000")
var shouldAskForShareDetails var shouldAskForShareDetails
by BooleanSetting(Constants.PREFERENCES_KEY_ASK_FOR_SHARE_DETAILS, true) by BooleanSetting(Constants.PREFERENCES_KEY_ASK_FOR_SHARE_DETAILS, true)
var defaultShareDescription var defaultShareDescription
by StringSetting(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION, "") by StringSetting(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION, "")
@JvmStatic @JvmStatic
val shareGreeting: String? val shareGreeting: String?

View File

@ -708,6 +708,7 @@ object Util {
return versionName return versionName
} }
@Suppress("DEPRECATION")
fun getVersionCode(context: Context): Int { fun getVersionCode(context: Context): Int {
var versionCode = 0 var versionCode = 0
val pm = context.packageManager val pm = context.packageManager

View File

@ -29,14 +29,14 @@
<string name="chat.send_a_message">Nachricht senden</string> <string name="chat.send_a_message">Nachricht senden</string>
<string name="common.album">Album</string> <string name="common.album">Album</string>
<string name="common.appname">Ultrasonic</string> <string name="common.appname">Ultrasonic</string>
<string name="common.artist">Künstler*in</string> <string name="common.artist">Künstler</string>
<string name="common.cancel">Abbrechen</string> <string name="common.cancel">Abbrechen</string>
<string name="common.comment">Kommentar</string> <string name="common.comment">Kommentar</string>
<string name="common.confirm">Bestätigen</string> <string name="common.confirm">Bestätigen</string>
<string name="common.delete">Löschen</string> <string name="common.delete">Löschen</string>
<string name="common.download">Herunterladen</string> <string name="common.download">Herunterladen</string>
<string name="common.info">Details</string> <string name="common.info">Details</string>
<string name="common.multiple_genres">Mehrere Genre</string> <string name="common.multiple_genres">Mehrere Genres</string>
<string name="common.name">Name</string> <string name="common.name">Name</string>
<string name="common.ok">OK</string> <string name="common.ok">OK</string>
<string name="common.pin">Anheften</string> <string name="common.pin">Anheften</string>
@ -44,15 +44,14 @@
<string name="common.play">Abspielen</string> <string name="common.play">Abspielen</string>
<string name="common.play_last">Zuletzt spielen</string> <string name="common.play_last">Zuletzt spielen</string>
<string name="common.play_next">Als nächstes spielen</string> <string name="common.play_next">Als nächstes spielen</string>
<string name="common.play_previous">Vorheriges abspielen <string name="common.play_previous">Vorheriges abspielen</string>
</string>
<string name="common.play_now">Jetzt spielen</string> <string name="common.play_now">Jetzt spielen</string>
<string name="common.play_shuffled">Zufällig spielen</string> <string name="common.play_shuffled">Zufällig spielen</string>
<string name="common.public">Öffentlich</string> <string name="common.public">Öffentlich</string>
<string name="common.save">Speichern</string> <string name="common.save">Speichern</string>
<string name="common.title">Titel</string> <string name="common.title">Titel</string>
<string name="common.unpin">Lösen</string> <string name="common.unpin">Lösen</string>
<string name="common.various_artists">Verschiedene Künstler*innen</string> <string name="common.various_artists">Verschiedene Künstler</string>
<string name="delete_playlist">Möchtest du %1$s löschen</string> <string name="delete_playlist">Möchtest du %1$s löschen</string>
<string name="download.bookmark_removed" formatted="false">Lesezeichen entfernt</string> <string name="download.bookmark_removed" formatted="false">Lesezeichen entfernt</string>
<string name="download.bookmark_set_at_position" formatted="false">Lesezeichen gesetzt als %s.</string> <string name="download.bookmark_set_at_position" formatted="false">Lesezeichen gesetzt als %s.</string>
@ -94,7 +93,7 @@
<string name="help.url">file:///android_asset/html/de/index.html</string> <string name="help.url">file:///android_asset/html/de/index.html</string>
<string name="jukebox.is_default">Jukebox als Standard</string> <string name="jukebox.is_default">Jukebox als Standard</string>
<string name="lyrics.nomatch">Keine Liedtexte gefunden</string> <string name="lyrics.nomatch">Keine Liedtexte gefunden</string>
<string name="main.albums_alphaByArtist">Nach Künstler*innen</string> <string name="main.albums_alphaByArtist">Nach Künstler</string>
<string name="main.albums_alphaByName">Nach Namen</string> <string name="main.albums_alphaByName">Nach Namen</string>
<string name="main.albums_frequent">Am häufigsten gespielt</string> <string name="main.albums_frequent">Am häufigsten gespielt</string>
<string name="main.albums_highest">Am besten bewertet</string> <string name="main.albums_highest">Am besten bewertet</string>
@ -103,10 +102,11 @@
<string name="main.albums_recent">Kürzlich gespielt</string> <string name="main.albums_recent">Kürzlich gespielt</string>
<string name="main.albums_starred">Mit Stern</string> <string name="main.albums_starred">Mit Stern</string>
<string name="main.albums_title">Alben</string> <string name="main.albums_title">Alben</string>
<string name="main.artists_title">Künstler*innen</string> <string name="main.artists_title">Künstler</string>
<string name="main.genres_title">Genres</string> <string name="main.genres_title">Genres</string>
<string name="main.music">Musik</string> <string name="main.music">Musik</string>
<string name="main.offline">Offline</string> <string name="main.offline">Offline</string>
<string name="main.setup_server">%s - Server einrichten</string>
<string name="main.shuffle">Gemischte Wiedergabe</string> <string name="main.shuffle">Gemischte Wiedergabe</string>
<string name="main.songs_random">Zufällig</string> <string name="main.songs_random">Zufällig</string>
<string name="main.songs_starred">Mit Stern</string> <string name="main.songs_starred">Mit Stern</string>
@ -125,7 +125,7 @@
<string name="music_library.label">Medienbibliothek</string> <string name="music_library.label">Medienbibliothek</string>
<string name="music_library.label_offline">Offline Medien</string> <string name="music_library.label_offline">Offline Medien</string>
<string name="music_service.retry">Netzwerkfehler. Neuer Versuch %1$d von %2$d.</string> <string name="music_service.retry">Netzwerkfehler. Neuer Versuch %1$d von %2$d.</string>
<string name="parser.artist_count">%d Künstler*in gefunden</string> <string name="parser.artist_count">%d Künstler gefunden</string>
<string name="parser.reading">Lese vom Server.</string> <string name="parser.reading">Lese vom Server.</string>
<string name="parser.reading_done">Lese vom Server. Fertig!</string> <string name="parser.reading_done">Lese vom Server. Fertig!</string>
<string name="playlist.label">Wiedergabelisten</string> <string name="playlist.label">Wiedergabelisten</string>
@ -134,7 +134,7 @@
<string name="playlist.updated_info_error">Aktualisierung der Wiedergabeliste %s ist fehlgeschlagen</string> <string name="playlist.updated_info_error">Aktualisierung der Wiedergabeliste %s ist fehlgeschlagen</string>
<string name="progress.wait">Bitte warten&#8230;</string> <string name="progress.wait">Bitte warten&#8230;</string>
<string name="search.albums">Alben</string> <string name="search.albums">Alben</string>
<string name="search.artists">Künstler*innen</string> <string name="search.artists">Künstler</string>
<string name="search.label">Suche</string> <string name="search.label">Suche</string>
<string name="search.more">Zeige mehr</string> <string name="search.more">Zeige mehr</string>
<string name="search.no_match">Keine Treffer, bitte erneut versuchen</string> <string name="search.no_match">Keine Treffer, bitte erneut versuchen</string>
@ -149,7 +149,7 @@
<string name="select_artist.folder">Ordner wählen</string> <string name="select_artist.folder">Ordner wählen</string>
<string name="select_genre.empty">Keine Genres gefunden</string> <string name="select_genre.empty">Keine Genres gefunden</string>
<string name="select_playlist.empty">Keine Wiedergabelisten auf dem Server</string> <string name="select_playlist.empty">Keine Wiedergabelisten auf dem Server</string>
<string name="service.connecting">Kontaktierse Server, bitte warten.</string> <string name="service.connecting">Kontaktiere Server, bitte warten.</string>
<string name="settings.appearance_title">Aussehen</string> <string name="settings.appearance_title">Aussehen</string>
<string name="settings.buffer_length">Puffer-Länge</string> <string name="settings.buffer_length">Puffer-Länge</string>
<string name="settings.buffer_length_0">Deaktiviert</string> <string name="settings.buffer_length_0">Deaktiviert</string>
@ -218,7 +218,7 @@
<string name="settings.invalid_url">Bitte eine gültige URL angeben.</string> <string name="settings.invalid_url">Bitte eine gültige URL angeben.</string>
<string name="settings.invalid_username">Bitte einen gültigen Benutzernamen eingeben (ohne führende Leerzeichen).</string> <string name="settings.invalid_username">Bitte einen gültigen Benutzernamen eingeben (ohne führende Leerzeichen).</string>
<string name="settings.max_albums">Maximale Alben</string> <string name="settings.max_albums">Maximale Alben</string>
<string name="settings.max_artists">Maximale Künstler*innen</string> <string name="settings.max_artists">Max Künstler</string>
<string name="settings.max_bitrate_112">112 Kbps</string> <string name="settings.max_bitrate_112">112 Kbps</string>
<string name="settings.max_bitrate_128">128 Kbps</string> <string name="settings.max_bitrate_128">128 Kbps</string>
<string name="settings.max_bitrate_160">160 Kbps</string> <string name="settings.max_bitrate_160">160 Kbps</string>
@ -229,12 +229,12 @@
<string name="settings.max_bitrate_64">64 Kbps</string> <string name="settings.max_bitrate_64">64 Kbps</string>
<string name="settings.max_bitrate_80">80 Kbps</string> <string name="settings.max_bitrate_80">80 Kbps</string>
<string name="settings.max_bitrate_96">96 Kbps</string> <string name="settings.max_bitrate_96">96 Kbps</string>
<string name="settings.max_bitrate_mobile">Maximale Bitragte - Mobil</string> <string name="settings.max_bitrate_mobile">Max Bitrate - Mobil</string>
<string name="settings.max_bitrate_unlimited">Unbegrenzt</string> <string name="settings.max_bitrate_unlimited">Unbegrenzt</string>
<string name="settings.max_bitrate_wifi">Maximale Bitrate - WLAN</string> <string name="settings.max_bitrate_wifi">Max Bitrate - WLAN</string>
<string name="settings.max_songs">Maximale Titel</string> <string name="settings.max_songs">Maximale Titel</string>
<string name="settings.media_button_summary">Auf Telefon, Headset und Bluetooth-Media-Tasten reagieren</string> <string name="settings.media_button_summary">Auf Telefon, Headset und Bluetooth-Media-Tasten reagieren</string>
<string name="settings.media_button_title">Media Tasten</string> <string name="settings.media_button_title">Medien Tasten</string>
<string name="settings.network_timeout">Netzwerk Zeitüberschreitung</string> <string name="settings.network_timeout">Netzwerk Zeitüberschreitung</string>
<string name="settings.network_timeout_105000">105 Sekunden</string> <string name="settings.network_timeout_105000">105 Sekunden</string>
<string name="settings.network_timeout_120000">120 Sekunden</string> <string name="settings.network_timeout_120000">120 Sekunden</string>
@ -257,6 +257,7 @@
<string name="settings.preload_unlimited">Unbegrenzt</string> <string name="settings.preload_unlimited">Unbegrenzt</string>
<string name="settings.playback.resume_play_on_headphones_plug.title">Fortsetzen mit Kopfhörer</string> <string name="settings.playback.resume_play_on_headphones_plug.title">Fortsetzen mit Kopfhörer</string>
<string name="settings.playback.resume_play_on_headphones_plug.summary">Die App setzt eine pausierte Wiedergabe beim Anschließen der Kopfhörer fort.</string> <string name="settings.playback.resume_play_on_headphones_plug.summary">Die App setzt eine pausierte Wiedergabe beim Anschließen der Kopfhörer fort.</string>
<string name="settings.scrobble_title">Gespielte Musik scrobbeln</string>
<string name="settings.search_1">1</string> <string name="settings.search_1">1</string>
<string name="settings.search_10">10</string> <string name="settings.search_10">10</string>
<string name="settings.search_100">100</string> <string name="settings.search_100">100</string>
@ -301,6 +302,7 @@
<string name="settings.testing_unlicensed">Verbindung OK, Server nicht lizensiert.</string> <string name="settings.testing_unlicensed">Verbindung OK, Server nicht lizensiert.</string>
<string name="settings.theme_light">Hell</string> <string name="settings.theme_light">Hell</string>
<string name="settings.theme_dark">Dunkel</string> <string name="settings.theme_dark">Dunkel</string>
<string name="settings.theme_black">Schwarz</string>
<string name="settings.theme_title">Thema</string> <string name="settings.theme_title">Thema</string>
<string name="settings.title.allow_self_signed_certificate">Selbst-signierte HTTPS Zertifikate erlauben</string> <string name="settings.title.allow_self_signed_certificate">Selbst-signierte HTTPS Zertifikate erlauben</string>
<string name="settings.title.enable_ldap_users_support">LDAP Benutzeranmeldung aktivieren</string> <string name="settings.title.enable_ldap_users_support">LDAP Benutzeranmeldung aktivieren</string>
@ -310,6 +312,8 @@
<string name="settings.use_id3">Durchsuchen von ID3-Tags</string> <string name="settings.use_id3">Durchsuchen von ID3-Tags</string>
<string name="settings.use_id3_summary">Nutze ID3 Tag Methode anstatt Dateisystem-Methode</string> <string name="settings.use_id3_summary">Nutze ID3 Tag Methode anstatt Dateisystem-Methode</string>
<string name="main.video" tools:ignore="UnusedResources">Film</string> <string name="main.video" tools:ignore="UnusedResources">Film</string>
<string name="settings.wifi_required_summary">Medien nur über gebührenfreie Verbindungen herunterladen</string>
<string name="settings.wifi_required_title">Nur über WLAN herunterladen</string>
<string name="song_details.all">%1$s%2$s</string> <string name="song_details.all">%1$s%2$s</string>
<string name="song_details.kbps">%d kbps</string> <string name="song_details.kbps">%d kbps</string>
<string name="util.bytes_format.byte">0 B</string> <string name="util.bytes_format.byte">0 B</string>
@ -322,11 +326,13 @@
<string name="widget.sdcard_busy">SD Karte nicht verfügbar</string> <string name="widget.sdcard_busy">SD Karte nicht verfügbar</string>
<string name="widget.sdcard_missing">Keine SD Karte</string> <string name="widget.sdcard_missing">Keine SD Karte</string>
<string name="settings.share_description_default">Standard Beschreibung einer Freigabe</string> <string name="settings.share_description_default">Standard Beschreibung einer Freigabe</string>
<string name="settings.sharing_title">Teilen</string> <string name="settings.sharing_title">Freigaben</string>
<string name="settings.sharing_always_ask_for_details">Immer nach Details fragen</string> <string name="settings.sharing_always_ask_for_details">Immer nach Details fragen</string>
<string name="settings.share_expiration_default">Standard Ablaufzeit</string> <string name="settings.share_expiration_default">Standard Ablaufzeit</string>
<string name="do_not_show_dialog_again">Dialog nicht wieder anzeigen</string> <string name="do_not_show_dialog_again">Dialog nicht wieder anzeigen</string>
<string name="share_set_share_options">Ferigabeoptionen</string> <string name="share_set_share_options">Freigabeoptionen</string>
<string name="share_on_server">Freigabe auf Server erstellen</string>
<string name="settings.share_on_server_summary">Teilen erzeugt eine Freigabe auf dem Server und sendet die URL. Wenn ausgeschaltet, werden nur die Lied-Details geteilt.</string>
<string name="no_expiration">Kein Ablaufdatum</string> <string name="no_expiration">Kein Ablaufdatum</string>
<string name="download.toggle_playlist">Wiedergabeliste umschalten</string> <string name="download.toggle_playlist">Wiedergabeliste umschalten</string>
<string name="download.bookmark_set">Lesezeichen setzen</string> <string name="download.bookmark_set">Lesezeichen setzen</string>
@ -350,13 +356,13 @@
<string name="download_song_removed">%s wurde von der Wiedergabeliste entfernt</string> <string name="download_song_removed">%s wurde von der Wiedergabeliste entfernt</string>
<string name="download.share_playlist">Wiedergabeliste teilen</string> <string name="download.share_playlist">Wiedergabeliste teilen</string>
<string name="download.share_song">Aktuelles Lied teilen</string> <string name="download.share_song">Aktuelles Lied teilen</string>
<string name="settings.share_greeting_default">Standard Begrüßung beim Teilen</string> <string name="settings.share_greeting_default">Standard Freigabe-Begrüßung</string>
<string name="share_default_greeting">Hör dir mal die Musik an, die ich mit dir über %s geteilt habe.</string> <string name="share_default_greeting">Hör dir mal die Musik an, die ich mit dir über %s geteilt habe.</string>
<string name="share_via">Titel teilen über</string> <string name="share_via">Titel teilen über</string>
<string name="menu.share">Freigabe</string> <string name="menu.share">Freigabe</string>
<string name="settings.show_all_songs_by_artist">Alle Titel nach Künstler*innen sortieren</string> <string name="settings.show_all_songs_by_artist">Alle Titel nach Künstler sortieren</string>
<string name="settings.show_all_songs_by_artist_summary">Einen neuen Eintrag in der Künstleransicht hinzufügen, um auf alle Lieder eines Künstlers zuzugreifen</string> <string name="settings.show_all_songs_by_artist_summary">Einen neuen Eintrag in der Künstleransicht hinzufügen, um auf alle Lieder eines Künstlers zuzugreifen</string>
<string name="download.menu_show_artist">Künstler*in zeigen</string> <string name="download.menu_show_artist">Künstler zeigen</string>
<string name="common_multiple_years">Mehrere Jahre</string> <string name="common_multiple_years">Mehrere Jahre</string>
<string name="settings.playback.resume_on_bluetooth_device">Wiedergabe fortsetzen, wenn ein Bluetooth Gerät verbunden wurde</string> <string name="settings.playback.resume_on_bluetooth_device">Wiedergabe fortsetzen, wenn ein Bluetooth Gerät verbunden wurde</string>
<string name="settings.playback.pause_on_bluetooth_device">Wiedergabe pausieren, wenn ein Bluetooth Gerät getrennt wurde</string> <string name="settings.playback.pause_on_bluetooth_device">Wiedergabe pausieren, wenn ein Bluetooth Gerät getrennt wurde</string>
@ -379,4 +385,9 @@
<string name="api.subsonic.upgrade_client">Inkompatible Versionen. Bitte die Ultrasonic App aktualisieren.</string> <string name="api.subsonic.upgrade_client">Inkompatible Versionen. Bitte die Ultrasonic App aktualisieren.</string>
<string name="api.subsonic.upgrade_server">Inkompatible Versionen. Bitte den subsonic Server aktualisieren.</string> <string name="api.subsonic.upgrade_server">Inkompatible Versionen. Bitte den subsonic Server aktualisieren.</string>
</resources> <!-- Subsonic features -->
<string name="settings.features_title">Besonderheiten</string>
<string name="settings.five_star_rating_title">Fünf-Stern Bewertung</string>
<string name="settings.five_star_rating_description">Benutze Bewertungssystem mit fünf Sternen anstatt Lieder mit bloß einem Stern zu markieren.</string>
</resources>