Merge branch 'develop' into update_circleci_img
This commit is contained in:
commit
2aa5174fbd
|
@ -8,7 +8,8 @@ import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import okhttp3.mockwebserver.MockResponse
|
import okhttp3.mockwebserver.MockResponse
|
||||||
import okhttp3.mockwebserver.MockWebServer
|
import okhttp3.mockwebserver.MockWebServer
|
||||||
import okio.Okio
|
import okio.buffer
|
||||||
|
import okio.source
|
||||||
import org.amshove.kluent.`should be`
|
import org.amshove.kluent.`should be`
|
||||||
import org.amshove.kluent.`should contain`
|
import org.amshove.kluent.`should contain`
|
||||||
import org.amshove.kluent.`should not be`
|
import org.amshove.kluent.`should not be`
|
||||||
|
@ -40,12 +41,12 @@ fun MockWebServer.enqueueResponse(resourceName: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Any.loadJsonResponse(name: String): String {
|
fun Any.loadJsonResponse(name: String): String {
|
||||||
val source = Okio.buffer(Okio.source(javaClass.classLoader.getResourceAsStream(name)))
|
val source = javaClass.classLoader.getResourceAsStream(name)!!.source().buffer()
|
||||||
return source.readString(Charset.forName("UTF-8"))
|
return source.readString(Charset.forName("UTF-8"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Any.loadResourceStream(name: String): InputStream {
|
fun Any.loadResourceStream(name: String): InputStream {
|
||||||
val source = Okio.buffer(Okio.source(javaClass.classLoader.getResourceAsStream(name)))
|
val source = javaClass.classLoader.getResourceAsStream(name)!!.source().buffer()
|
||||||
return source.inputStream()
|
return source.inputStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ fun Response<out ResponseBody>.toStreamResponse(): StreamResponse {
|
||||||
val contentType = responseBody?.contentType()
|
val contentType = responseBody?.contentType()
|
||||||
if (
|
if (
|
||||||
contentType != null &&
|
contentType != null &&
|
||||||
contentType.type().equals("application", true) &&
|
contentType.type.equals("application", true) &&
|
||||||
contentType.subtype().equals("json", true)
|
contentType.subtype.equals("json", true)
|
||||||
) {
|
) {
|
||||||
val error = SubsonicAPIClient.jacksonMapper.readValue<SubsonicResponse>(
|
val error = SubsonicAPIClient.jacksonMapper.readValue<SubsonicResponse>(
|
||||||
responseBody.byteStream()
|
responseBody.byteStream()
|
||||||
|
@ -40,7 +40,7 @@ fun Response<out ResponseBody>.toStreamResponse(): StreamResponse {
|
||||||
* It creates Exceptions from the results returned by the Subsonic API
|
* It creates Exceptions from the results returned by the Subsonic API
|
||||||
*/
|
*/
|
||||||
@Suppress("ThrowsCount")
|
@Suppress("ThrowsCount")
|
||||||
fun <T : SubsonicResponse> Response<out T>.throwOnFailure(): Response<out T> {
|
fun <T : SubsonicResponse> Response<T>.throwOnFailure(): Response<T> {
|
||||||
val response = this
|
val response = this
|
||||||
|
|
||||||
if (response.isSuccessful && response.body()!!.status === SubsonicResponse.Status.OK) {
|
if (response.isSuccessful && response.body()!!.status === SubsonicResponse.Status.OK) {
|
||||||
|
|
|
@ -68,7 +68,7 @@ class SubsonicAPIClient(
|
||||||
.addInterceptor { chain ->
|
.addInterceptor { chain ->
|
||||||
// Adds default request params
|
// Adds default request params
|
||||||
val originalRequest = chain.request()
|
val originalRequest = chain.request()
|
||||||
val newUrl = originalRequest.url().newBuilder()
|
val newUrl = originalRequest.url.newBuilder()
|
||||||
.addQueryParameter("u", config.username)
|
.addQueryParameter("u", config.username)
|
||||||
.addQueryParameter("c", config.clientID)
|
.addQueryParameter("c", config.clientID)
|
||||||
.addQueryParameter("f", "json")
|
.addQueryParameter("f", "json")
|
||||||
|
|
|
@ -18,7 +18,7 @@ class PasswordHexInterceptor(private val password: String) : Interceptor {
|
||||||
|
|
||||||
override fun intercept(chain: Chain): Response {
|
override fun intercept(chain: Chain): Response {
|
||||||
val originalRequest = chain.request()
|
val originalRequest = chain.request()
|
||||||
val updatedUrl = originalRequest.url().newBuilder()
|
val updatedUrl = originalRequest.url.newBuilder()
|
||||||
.addEncodedQueryParameter("p", passwordHex).build()
|
.addEncodedQueryParameter("p", passwordHex).build()
|
||||||
return chain.proceed(originalRequest.newBuilder().url(updatedUrl).build())
|
return chain.proceed(originalRequest.newBuilder().url(updatedUrl).build())
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ class PasswordMD5Interceptor(private val password: String) : Interceptor {
|
||||||
override fun intercept(chain: Chain): Response {
|
override fun intercept(chain: Chain): Response {
|
||||||
val originalRequest = chain.request()
|
val originalRequest = chain.request()
|
||||||
val salt = getSalt()
|
val salt = getSalt()
|
||||||
val updatedUrl = originalRequest.url().newBuilder()
|
val updatedUrl = originalRequest.url.newBuilder()
|
||||||
.addQueryParameter("t", getPasswordMD5Hash(salt))
|
.addQueryParameter("t", getPasswordMD5Hash(salt))
|
||||||
.addQueryParameter("s", salt)
|
.addQueryParameter("s", salt)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -19,7 +19,7 @@ internal const val TIMEOUT_MILLIS_PER_OFFSET_BYTE = 0.02
|
||||||
internal class RangeHeaderInterceptor : Interceptor {
|
internal class RangeHeaderInterceptor : Interceptor {
|
||||||
override fun intercept(chain: Chain): Response {
|
override fun intercept(chain: Chain): Response {
|
||||||
val originalRequest = chain.request()
|
val originalRequest = chain.request()
|
||||||
val headers = originalRequest.headers()
|
val headers = originalRequest.headers
|
||||||
return if (headers.names().contains("Range")) {
|
return if (headers.names().contains("Range")) {
|
||||||
val offsetValue = headers["Range"] ?: "0"
|
val offsetValue = headers["Range"] ?: "0"
|
||||||
val offset = "bytes=$offsetValue-"
|
val offset = "bytes=$offsetValue-"
|
||||||
|
|
|
@ -18,7 +18,7 @@ internal class VersionInterceptor(
|
||||||
val newRequest = originalRequest.newBuilder()
|
val newRequest = originalRequest.newBuilder()
|
||||||
.url(
|
.url(
|
||||||
originalRequest
|
originalRequest
|
||||||
.url()
|
.url
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.addQueryParameter("v", protocolVersion.restApiVersion)
|
.addQueryParameter("v", protocolVersion.restApiVersion)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -22,9 +22,9 @@ kotlin = "1.6.10"
|
||||||
kotlinxCoroutines = "1.6.0-native-mt"
|
kotlinxCoroutines = "1.6.0-native-mt"
|
||||||
viewModelKtx = "2.3.0"
|
viewModelKtx = "2.3.0"
|
||||||
|
|
||||||
retrofit = "2.6.4"
|
retrofit = "2.9.0"
|
||||||
jackson = "2.9.5"
|
jackson = "2.10.1"
|
||||||
okhttp = "3.12.13"
|
okhttp = "4.9.1"
|
||||||
koin = "3.0.2"
|
koin = "3.0.2"
|
||||||
picasso = "2.71828"
|
picasso = "2.71828"
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,10 @@ android {
|
||||||
minSdkVersion versions.minSdk
|
minSdkVersion versions.minSdk
|
||||||
targetSdkVersion versions.targetSdk
|
targetSdkVersion versions.targetSdk
|
||||||
resConfigs 'cs', 'de', 'en', 'es', 'fr', 'hu', 'it', 'nl', 'pl', 'pt', 'pt-rBR', 'ru', 'zh-rCN', 'zh-rTW'
|
resConfigs 'cs', 'de', 'en', 'es', 'fr', 'hu', 'it', 'nl', 'pl', 'pt', 'pt-rBR', 'ru', 'zh-rCN', 'zh-rTW'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bundle.language.enableSplit = false
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
package org.moire.ultrasonic.activity
|
package org.moire.ultrasonic.activity
|
||||||
|
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
@ -53,6 +55,7 @@ import org.moire.ultrasonic.service.RxBus
|
||||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.InfoDialog
|
import org.moire.ultrasonic.util.InfoDialog
|
||||||
|
import org.moire.ultrasonic.util.LocaleHelper
|
||||||
import org.moire.ultrasonic.util.ServerColor
|
import org.moire.ultrasonic.util.ServerColor
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Storage
|
import org.moire.ultrasonic.util.Storage
|
||||||
|
@ -351,6 +354,15 @@ class NavigationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the customized language settings if needed
|
||||||
|
*/
|
||||||
|
override fun attachBaseContext(newBase: Context?) {
|
||||||
|
val locale = Settings.overrideLanguage
|
||||||
|
val localeUpdatedContext: ContextWrapper = LocaleHelper.wrap(newBase, locale)
|
||||||
|
super.attachBaseContext(localeUpdatedContext)
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadSettings() {
|
private fun loadSettings() {
|
||||||
PreferenceManager.setDefaultValues(this, R.xml.settings, false)
|
PreferenceManager.setDefaultValues(this, R.xml.settings, false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import com.squareup.picasso.Picasso
|
||||||
import com.squareup.picasso.Request
|
import com.squareup.picasso.Request
|
||||||
import com.squareup.picasso.RequestHandler
|
import com.squareup.picasso.RequestHandler
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import okio.Okio
|
import okio.source
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +29,7 @@ class AvatarRequestHandler(
|
||||||
if (response.hasError() || response.stream == null) {
|
if (response.hasError() || response.stream == null) {
|
||||||
throw IOException("${response.apiError}")
|
throw IOException("${response.apiError}")
|
||||||
} else {
|
} else {
|
||||||
return Result(Okio.source(response.stream!!), Picasso.LoadedFrom.NETWORK)
|
return Result(response.stream!!.source(), Picasso.LoadedFrom.NETWORK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import com.squareup.picasso.Picasso.LoadedFrom.NETWORK
|
||||||
import com.squareup.picasso.Request
|
import com.squareup.picasso.Request
|
||||||
import com.squareup.picasso.RequestHandler
|
import com.squareup.picasso.RequestHandler
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import okio.Okio
|
import okio.source
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||||
import org.moire.ultrasonic.api.subsonic.toStreamResponse
|
import org.moire.ultrasonic.api.subsonic.toStreamResponse
|
||||||
import org.moire.ultrasonic.util.FileUtil.SUFFIX_LARGE
|
import org.moire.ultrasonic.util.FileUtil.SUFFIX_LARGE
|
||||||
|
@ -44,7 +44,7 @@ class CoverArtRequestHandler(private val client: SubsonicAPIClient) : RequestHan
|
||||||
|
|
||||||
// Handle the response
|
// Handle the response
|
||||||
if (!response.hasError() && response.stream != null) {
|
if (!response.hasError() && response.stream != null) {
|
||||||
return Result(Okio.source(response.stream!!), NETWORK)
|
return Result(response.stream!!.source(), NETWORK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw an error if still not successful
|
// Throw an error if still not successful
|
||||||
|
|
|
@ -10,12 +10,11 @@ import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import okhttp3.Protocol
|
import okhttp3.Protocol
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
|
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.Companion.fromName
|
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.Companion.fromName
|
||||||
import org.moire.ultrasonic.api.subsonic.models.JukeboxAction
|
import org.moire.ultrasonic.api.subsonic.models.JukeboxAction
|
||||||
import org.moire.ultrasonic.api.subsonic.response.StreamResponse
|
|
||||||
import org.moire.ultrasonic.api.subsonic.throwOnFailure
|
import org.moire.ultrasonic.api.subsonic.throwOnFailure
|
||||||
import org.moire.ultrasonic.api.subsonic.toStreamResponse
|
import org.moire.ultrasonic.api.subsonic.toStreamResponse
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
|
@ -425,14 +424,13 @@ open class RESTMusicService(
|
||||||
save: Boolean
|
save: Boolean
|
||||||
): Pair<InputStream, Boolean> {
|
): Pair<InputStream, Boolean> {
|
||||||
val songOffset = if (offset < 0) 0 else offset
|
val songOffset = if (offset < 0) 0 else offset
|
||||||
lateinit var response: StreamResponse
|
|
||||||
|
|
||||||
// Use semantically correct call
|
// Use semantically correct call
|
||||||
if (save) {
|
val response = if (save) {
|
||||||
response = API.download(song.id, maxBitrate, offset = songOffset)
|
API.download(song.id, maxBitrate, offset = songOffset)
|
||||||
.execute().toStreamResponse()
|
.execute().toStreamResponse()
|
||||||
} else {
|
} else {
|
||||||
response = API.stream(song.id, maxBitrate, offset = songOffset)
|
API.stream(song.id, maxBitrate, offset = songOffset)
|
||||||
.execute().toStreamResponse()
|
.execute().toStreamResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +461,7 @@ open class RESTMusicService(
|
||||||
// Returns a dummy response
|
// Returns a dummy response
|
||||||
Response.Builder()
|
Response.Builder()
|
||||||
.code(100)
|
.code(100)
|
||||||
.body(ResponseBody.create(null, ""))
|
.body("".toResponseBody(null))
|
||||||
.protocol(Protocol.HTTP_2)
|
.protocol(Protocol.HTTP_2)
|
||||||
.message("Empty response")
|
.message("Empty response")
|
||||||
.request(chain.request())
|
.request(chain.request())
|
||||||
|
@ -480,7 +478,7 @@ open class RESTMusicService(
|
||||||
val response = client.newCall(request).execute()
|
val response = client.newCall(request).execute()
|
||||||
|
|
||||||
// The complete url :)
|
// The complete url :)
|
||||||
val url = response.request().url()
|
val url = response.request.url
|
||||||
|
|
||||||
return url.toString()
|
return url.toString()
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,7 @@ object Constants {
|
||||||
const val PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE = "pauseOnBluetoothDevice"
|
const val PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE = "pauseOnBluetoothDevice"
|
||||||
const val PREFERENCES_KEY_SINGLE_BUTTON_PLAY_PAUSE = "singleButtonPlayPause"
|
const val PREFERENCES_KEY_SINGLE_BUTTON_PLAY_PAUSE = "singleButtonPlayPause"
|
||||||
const val PREFERENCES_KEY_DEBUG_LOG_TO_FILE = "debugLogToFile"
|
const val PREFERENCES_KEY_DEBUG_LOG_TO_FILE = "debugLogToFile"
|
||||||
|
const val PREFERENCES_KEY_OVERRIDE_LANGUAGE = "overrideLanguage"
|
||||||
const val PREFERENCE_VALUE_ALL = 0
|
const val PREFERENCE_VALUE_ALL = 0
|
||||||
const val PREFERENCE_VALUE_A2DP = 1
|
const val PREFERENCE_VALUE_A2DP = 1
|
||||||
const val PREFERENCE_VALUE_DISABLED = 2
|
const val PREFERENCE_VALUE_DISABLED = 2
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* LocaleHelper.kt
|
||||||
|
* Copyright (C) 2009-2021 Ultrasonic developers
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.moire.ultrasonic.util
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple Helper class to "wrap" a context with a new locale.
|
||||||
|
*/
|
||||||
|
class LocaleHelper(base: Context?) : ContextWrapper(base) {
|
||||||
|
companion object {
|
||||||
|
fun wrap(ctx: Context?, language: String): ContextWrapper {
|
||||||
|
var context = ctx
|
||||||
|
if (context != null && language != "") {
|
||||||
|
val config = context.resources.configuration
|
||||||
|
val locale = Locale.forLanguageTag(language)
|
||||||
|
Locale.setDefault(locale)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
setSystemLocale(config, locale)
|
||||||
|
} else {
|
||||||
|
setSystemLocaleLegacy(config, locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.setLayoutDirection(locale)
|
||||||
|
context = context.createConfigurationContext(config)
|
||||||
|
}
|
||||||
|
return LocaleHelper(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun setSystemLocaleLegacy(config: Configuration, locale: Locale?) {
|
||||||
|
config.locale = locale
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
|
fun setSystemLocale(config: Configuration, locale: Locale?) {
|
||||||
|
config.setLocale(locale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -308,12 +308,16 @@ object Settings {
|
||||||
Constants.PREFERENCE_VALUE_A2DP
|
Constants.PREFERENCE_VALUE_A2DP
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
var debugLogToFile by BooleanSetting(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE, false)
|
var debugLogToFile by BooleanSetting(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE, false)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val preferences: SharedPreferences
|
val preferences: SharedPreferences
|
||||||
get() = PreferenceManager.getDefaultSharedPreferences(Util.appContext())
|
get() = PreferenceManager.getDefaultSharedPreferences(Util.appContext())
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val overrideLanguage by StringSetting(Constants.PREFERENCES_KEY_OVERRIDE_LANGUAGE, "")
|
||||||
|
|
||||||
var useFiveStarRating by BooleanSetting(Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING, false)
|
var useFiveStarRating by BooleanSetting(Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING, false)
|
||||||
|
|
||||||
// TODO: Remove in December 2022
|
// TODO: Remove in December 2022
|
||||||
|
|
|
@ -399,7 +399,6 @@
|
||||||
<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>
|
||||||
|
|
||||||
<!-- Subsonic features -->
|
<!-- 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_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>
|
<string name="settings.five_star_rating_description">Benutze Bewertungssystem mit fünf Sternen anstatt Lieder mit bloß einem Stern zu markieren.</string>
|
||||||
|
|
||||||
|
|
|
@ -461,7 +461,6 @@
|
||||||
<string name="api.subsonic.upgrade_server">Versiones incompatibles. Por favor actualiza el servidor de Subsonic.</string>
|
<string name="api.subsonic.upgrade_server">Versiones incompatibles. Por favor actualiza el servidor de Subsonic.</string>
|
||||||
|
|
||||||
<!-- Subsonic features -->
|
<!-- Subsonic features -->
|
||||||
<string name="settings.features_title">Características</string>
|
|
||||||
<string name="settings.five_star_rating_title">Use cinco estrellas para las canciones</string>
|
<string name="settings.five_star_rating_title">Use cinco estrellas para las canciones</string>
|
||||||
<string name="settings.five_star_rating_description">Utilice el sistema de calificación de cinco estrellas para canciones
|
<string name="settings.five_star_rating_description">Utilice el sistema de calificación de cinco estrellas para canciones
|
||||||
en lugar de simplemente marcar / desmarcar elementos.
|
en lugar de simplemente marcar / desmarcar elementos.
|
||||||
|
|
|
@ -461,7 +461,6 @@
|
||||||
<string name="api.subsonic.upgrade_server">Incompatibele versies. Werk je Subsonic-server bij.</string>
|
<string name="api.subsonic.upgrade_server">Incompatibele versies. Werk je Subsonic-server bij.</string>
|
||||||
|
|
||||||
<!-- Subsonic features -->
|
<!-- Subsonic features -->
|
||||||
<string name="settings.features_title">Functies</string>
|
|
||||||
<string name="settings.five_star_rating_title">Vijf sterren gebruiken voor nummers</string>
|
<string name="settings.five_star_rating_title">Vijf sterren gebruiken voor nummers</string>
|
||||||
<string name="settings.five_star_rating_description">Toon vijf sterren om nummers te beoordelen
|
<string name="settings.five_star_rating_description">Toon vijf sterren om nummers te beoordelen
|
||||||
in plaats van items toe te voegen aan of te verwijderen uit de favorieten.
|
in plaats van items toe te voegen aan of te verwijderen uit de favorieten.
|
||||||
|
|
|
@ -444,7 +444,6 @@
|
||||||
<string name="api.subsonic.upgrade_server">不兼容的版本。请升级Subsonic 服务。</string>
|
<string name="api.subsonic.upgrade_server">不兼容的版本。请升级Subsonic 服务。</string>
|
||||||
|
|
||||||
<!-- Subsonic features -->
|
<!-- Subsonic features -->
|
||||||
<string name="settings.features_title">特性</string>
|
|
||||||
<string name="settings.five_star_rating_title">为歌曲使用五星评分</string>
|
<string name="settings.five_star_rating_title">为歌曲使用五星评分</string>
|
||||||
<string name="settings.five_star_rating_description">对歌曲使用五星级评级系统
|
<string name="settings.five_star_rating_description">对歌曲使用五星级评级系统
|
||||||
而不是简单地为项目加星标/取消星标。</string>
|
而不是简单地为项目加星标/取消星标。</string>
|
||||||
|
|
|
@ -236,5 +236,40 @@
|
||||||
<item>@string/settings.playback.bluetooth_a2dp</item>
|
<item>@string/settings.playback.bluetooth_a2dp</item>
|
||||||
<item>@string/settings.playback.bluetooth_disabled</item>
|
<item>@string/settings.playback.bluetooth_disabled</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="languageNames" translatable="false">
|
||||||
|
<item>@string/language.default</item>
|
||||||
|
<item>@string/language.zh_CN</item>
|
||||||
|
<item>@string/language.zh_TW</item>
|
||||||
|
<item>@string/language.cs</item>
|
||||||
|
<item>@string/language.nl</item>
|
||||||
|
<item>@string/language.en</item>
|
||||||
|
<item>@string/language.fr</item>
|
||||||
|
<item>@string/language.de</item>
|
||||||
|
<item>@string/language.hu</item>
|
||||||
|
<item>@string/language.it</item>
|
||||||
|
<item>@string/language.es</item>
|
||||||
|
<item>@string/language.pl</item>
|
||||||
|
<item>@string/language.pt</item>
|
||||||
|
<item>@string/language.pt_BR</item>
|
||||||
|
<item>@string/language.ru</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="languageValues" translatable="false">
|
||||||
|
<!--suppress CheckTagEmptyBody -->
|
||||||
|
<item></item>
|
||||||
|
<item>zh-CN</item>
|
||||||
|
<item>zh-TW</item>
|
||||||
|
<item>cs</item>
|
||||||
|
<item>nl</item>
|
||||||
|
<item>en</item>
|
||||||
|
<item>fr</item>
|
||||||
|
<item>de</item>
|
||||||
|
<item>hu</item>
|
||||||
|
<item>it</item>
|
||||||
|
<item>es</item>
|
||||||
|
<item>pl</item>
|
||||||
|
<item>pt</item>
|
||||||
|
<item>pt-BR</item>
|
||||||
|
<item>ru</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
|
@ -96,6 +96,21 @@
|
||||||
<string name="error.label">Error</string>
|
<string name="error.label">Error</string>
|
||||||
<string name="jukebox.is_default">Jukebox By Default</string>
|
<string name="jukebox.is_default">Jukebox By Default</string>
|
||||||
<string name="lyrics.nomatch">No lyrics found</string>
|
<string name="lyrics.nomatch">No lyrics found</string>
|
||||||
|
<string name="language.default">System default</string>
|
||||||
|
<string name="language.zh_CN">Chinese (China)</string>
|
||||||
|
<string name="language.zh_TW">Chinese (Taiwan)</string>
|
||||||
|
<string name="language.cs">Czech</string>
|
||||||
|
<string name="language.nl">Dutch</string>
|
||||||
|
<string name="language.en">English</string>
|
||||||
|
<string name="language.fr">French</string>
|
||||||
|
<string name="language.de">German</string>
|
||||||
|
<string name="language.hu">Hungarian</string>
|
||||||
|
<string name="language.it">Italian</string>
|
||||||
|
<string name="language.es">Spanish</string>
|
||||||
|
<string name="language.pl">Polish</string>
|
||||||
|
<string name="language.pt">Portuguese</string>
|
||||||
|
<string name="language.pt_BR">Portuguese (Brazil)</string>
|
||||||
|
<string name="language.ru">Russian</string>
|
||||||
<string name="main.albums_alphaByArtist">By Artist</string>
|
<string name="main.albums_alphaByArtist">By Artist</string>
|
||||||
<string name="main.albums_alphaByName">By Name</string>
|
<string name="main.albums_alphaByName">By Name</string>
|
||||||
<string name="main.albums_frequent">Most Played</string>
|
<string name="main.albums_frequent">Most Played</string>
|
||||||
|
@ -148,7 +163,7 @@
|
||||||
<string name="search.title">Search</string>
|
<string name="search.title">Search</string>
|
||||||
<string name="select_album.empty">No media found</string>
|
<string name="select_album.empty">No media found</string>
|
||||||
<string name="select_album.n_selected">%d tracks selected</string>
|
<string name="select_album.n_selected">%d tracks selected</string>
|
||||||
<string name="select_album.no_network">Warning: No network available.</string>
|
<string name="select_album.no_network">Warning: No usable network available.\n If you are using mobile data, you may need to allow downloads on metered connections in the settings.</string>
|
||||||
<string name="select_album.no_sdcard">Error: No SD card available.</string>
|
<string name="select_album.no_sdcard">Error: No SD card available.</string>
|
||||||
<string name="select_album.play_all">Play All</string>
|
<string name="select_album.play_all">Play All</string>
|
||||||
<string name="select_artist.all_folders">All Folders</string>
|
<string name="select_artist.all_folders">All Folders</string>
|
||||||
|
@ -257,6 +272,8 @@
|
||||||
<string name="settings.notifications_title">Notifications</string>
|
<string name="settings.notifications_title">Notifications</string>
|
||||||
<string name="settings.network_title">Network</string>
|
<string name="settings.network_title">Network</string>
|
||||||
<string name="settings.other_title">Other Settings</string>
|
<string name="settings.other_title">Other Settings</string>
|
||||||
|
<string name="settings.override_language">Override the language</string>
|
||||||
|
<string name="settings.override_language_summary">You need to restart the app after changing the language</string>
|
||||||
<string name="settings.playback_control_title">Playback Control Settings</string>
|
<string name="settings.playback_control_title">Playback Control Settings</string>
|
||||||
<string name="settings.preload">Songs To Preload</string>
|
<string name="settings.preload">Songs To Preload</string>
|
||||||
<string name="settings.preload_1">1 song</string>
|
<string name="settings.preload_1">1 song</string>
|
||||||
|
@ -466,7 +483,6 @@
|
||||||
<string name="api.subsonic.upgrade_server">Incompatible versions. Please upgrade Subsonic server.</string>
|
<string name="api.subsonic.upgrade_server">Incompatible versions. Please upgrade Subsonic server.</string>
|
||||||
|
|
||||||
<!-- Subsonic features -->
|
<!-- Subsonic features -->
|
||||||
<string name="settings.features_title">Features</string>
|
|
||||||
<string name="settings.five_star_rating_title">Use five star rating for songs</string>
|
<string name="settings.five_star_rating_title">Use five star rating for songs</string>
|
||||||
<string name="settings.five_star_rating_description">Use five star rating system for songs instead of simply starring/unstarring items.
|
<string name="settings.five_star_rating_description">Use five star rating system for songs instead of simply starring/unstarring items.
|
||||||
</string>
|
</string>
|
||||||
|
|
|
@ -122,6 +122,12 @@
|
||||||
a:summary="@string/settings.playback.single_button_bluetooth_device_summary"
|
a:summary="@string/settings.playback.single_button_bluetooth_device_summary"
|
||||||
a:title="@string/settings.playback.single_button_bluetooth_device"
|
a:title="@string/settings.playback.single_button_bluetooth_device"
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
a:defaultValue="false"
|
||||||
|
a:key="use_five_star_rating"
|
||||||
|
a:summary="@string/settings.five_star_rating_description"
|
||||||
|
a:title="@string/settings.five_star_rating_title"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
a:title="@string/settings.notifications_title"
|
a:title="@string/settings.notifications_title"
|
||||||
|
@ -343,16 +349,14 @@
|
||||||
a:summary="@string/settings.hide_media_summary"
|
a:summary="@string/settings.hide_media_summary"
|
||||||
a:title="@string/settings.hide_media_title"
|
a:title="@string/settings.hide_media_title"
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false"/>
|
||||||
</PreferenceCategory>
|
<ListPreference
|
||||||
<PreferenceCategory
|
a:defaultValue=""
|
||||||
a:title="@string/settings.features_title"
|
a:entries="@array/languageNames"
|
||||||
app:iconSpaceReserved="false">
|
a:entryValues="@array/languageValues"
|
||||||
<CheckBoxPreference
|
a:key="overrideLanguage"
|
||||||
a:defaultValue="false"
|
a:title="@string/settings.override_language"
|
||||||
a:key="use_five_star_rating"
|
a:summary="@string/settings.override_language_summary"
|
||||||
a:summary="@string/settings.five_star_rating_description"
|
app:iconSpaceReserved="false"/>
|
||||||
a:title="@string/settings.five_star_rating_title"
|
|
||||||
app:iconSpaceReserved="false" />
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
a:title="@string/settings.debug.title"
|
a:title="@string/settings.debug.title"
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package org.moire.ultrasonic.imageloader
|
package org.moire.ultrasonic.imageloader
|
||||||
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import okio.Okio
|
import okio.buffer
|
||||||
|
import okio.source
|
||||||
|
|
||||||
fun Any.loadResourceStream(name: String): InputStream {
|
fun Any.loadResourceStream(name: String): InputStream {
|
||||||
val source = Okio.buffer(Okio.source(javaClass.classLoader!!.getResourceAsStream(name)))
|
val source = javaClass.classLoader!!.getResourceAsStream(name).source().buffer()
|
||||||
return source.inputStream()
|
return source.inputStream()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue