Merge pull request #505 from Maxmystere/check-server-features

Implement server feature checking
This commit is contained in:
Nite 2021-06-01 15:37:56 +02:00 committed by GitHub
commit 0a886d7095
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 149 additions and 38 deletions

View File

@ -594,16 +594,16 @@ public class Util
} }
private static void showDialog(Context context, int icon, int titleId, int messageId) // The AlertDialog requires an Activity context, app context is not enough
// See https://stackoverflow.com/questions/5436822/
public static void showDialog(Context context, int icon, int titleId, String message)
{ {
new AlertDialog.Builder(context).setIcon(icon).setTitle(titleId).setMessage(messageId).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() new AlertDialog.Builder(context)
{ .setIcon(icon)
@Override .setTitle(titleId)
public void onClick(DialogInterface dialog, int i) .setMessage(message)
{ .setPositiveButton(R.string.common_ok, (dialog, i) -> dialog.dismiss())
dialog.dismiss(); .show();
}
}).show();
} }

View File

@ -29,7 +29,9 @@ import androidx.preference.PreferenceManager
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.java.KoinJavaComponent.inject
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.fragment.OnBackPressedHandler import org.moire.ultrasonic.fragment.OnBackPressedHandler
@ -368,10 +370,18 @@ class NavigationActivity : AppCompatActivity() {
} }
private fun setMenuForServerSetting() { private fun setMenuForServerSetting() {
val visibility = !isOffline() if (isOffline()) {
chatMenuItem?.isVisible = visibility chatMenuItem?.isVisible = false
bookmarksMenuItem?.isVisible = visibility bookmarksMenuItem?.isVisible = false
sharesMenuItem?.isVisible = visibility sharesMenuItem?.isVisible = false
podcastsMenuItem?.isVisible = visibility podcastsMenuItem?.isVisible = false
return
}
val activeServerProvider: ActiveServerProvider by inject()
val activeServer = activeServerProvider.getActiveServer()
chatMenuItem?.isVisible = activeServer.chatSupport != false
bookmarksMenuItem?.isVisible = activeServer.bookmarkSupport != false
sharesMenuItem?.isVisible = activeServer.shareSupport != false
podcastsMenuItem?.isVisible = activeServer.podcastSupport != false
} }
} }

View File

@ -8,7 +8,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
/** /**
* Room Database to be used to store data for Ultrasonic * Room Database to be used to store data for Ultrasonic
*/ */
@Database(entities = [ServerSetting::class], version = 2) @Database(entities = [ServerSetting::class], version = 3)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
/** /**
@ -24,3 +24,20 @@ val MIGRATION_1_2: Migration = object : Migration(1, 2) {
) )
} }
} }
val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"ALTER TABLE ServerSetting ADD COLUMN chatSupport INTEGER"
)
database.execSQL(
"ALTER TABLE ServerSetting ADD COLUMN bookmarkSupport INTEGER"
)
database.execSQL(
"ALTER TABLE ServerSetting ADD COLUMN shareSupport INTEGER"
)
database.execSQL(
"ALTER TABLE ServerSetting ADD COLUMN podcastSupport INTEGER"
)
}
}

View File

@ -29,7 +29,11 @@ data class ServerSetting(
@ColumnInfo(name = "allowSelfSignedCertificate") var allowSelfSignedCertificate: Boolean, @ColumnInfo(name = "allowSelfSignedCertificate") var allowSelfSignedCertificate: Boolean,
@ColumnInfo(name = "ldapSupport") var ldapSupport: Boolean, @ColumnInfo(name = "ldapSupport") var ldapSupport: Boolean,
@ColumnInfo(name = "musicFolderId") var musicFolderId: String?, @ColumnInfo(name = "musicFolderId") var musicFolderId: String?,
@ColumnInfo(name = "minimumApiVersion") var minimumApiVersion: String? @ColumnInfo(name = "minimumApiVersion") var minimumApiVersion: String?,
@ColumnInfo(name = "chatSupport") var chatSupport: Boolean? = null,
@ColumnInfo(name = "bookmarkSupport") var bookmarkSupport: Boolean? = null,
@ColumnInfo(name = "shareSupport") var shareSupport: Boolean? = null,
@ColumnInfo(name = "podcastSupport") var podcastSupport: Boolean? = null
) { ) {
constructor() : this ( constructor() : this (
-1, 0, "", "", "", "", false, false, false, null, null -1, 0, "", "", "", "", false, false, false, null, null

View File

@ -7,6 +7,7 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
import org.moire.ultrasonic.data.AppDatabase import org.moire.ultrasonic.data.AppDatabase
import org.moire.ultrasonic.data.MIGRATION_1_2 import org.moire.ultrasonic.data.MIGRATION_1_2
import org.moire.ultrasonic.data.MIGRATION_2_3
import org.moire.ultrasonic.fragment.ServerSettingsModel import org.moire.ultrasonic.fragment.ServerSettingsModel
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
@ -25,6 +26,7 @@ val appPermanentStorage = module {
"ultrasonic-database" "ultrasonic-database"
) )
.addMigrations(MIGRATION_1_2) .addMigrations(MIGRATION_1_2)
.addMigrations(MIGRATION_2_3)
.fallbackToDestructiveMigrationOnDowngrade() .fallbackToDestructiveMigrationOnDowngrade()
.build() .build()
} }

View File

@ -11,8 +11,10 @@ import androidx.lifecycle.Observer
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 java.io.IOException
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
import java.util.Locale
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.BuildConfig
@ -20,14 +22,17 @@ import org.moire.ultrasonic.R
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ServerSetting import org.moire.ultrasonic.data.ServerSetting
import org.moire.ultrasonic.service.ApiCallResponseChecker import org.moire.ultrasonic.service.ApiCallResponseChecker
import org.moire.ultrasonic.service.MusicServiceFactory import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.service.SubsonicRESTException
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.ErrorDialog import org.moire.ultrasonic.util.ErrorDialog
import org.moire.ultrasonic.util.ModalBackgroundTask import org.moire.ultrasonic.util.ModalBackgroundTask
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import retrofit2.Response
import timber.log.Timber import timber.log.Timber
/** /**
@ -295,14 +300,41 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
* Tests if the network connection to the entered Server Settings can be made * Tests if the network connection to the entered Server Settings can be made
*/ */
private fun testConnection() { private fun testConnection() {
val task: ModalBackgroundTask<Boolean> = object : ModalBackgroundTask<Boolean>( val task: ModalBackgroundTask<String> = object : ModalBackgroundTask<String>(
activity, activity,
false false
) { ) {
fun boolToMark(value: Boolean?): String {
if (value == null)
return ""
return if (value) "✔️" else ""
}
fun getProgress(): String {
return String.format(
"""
|%s - ${resources.getString(R.string.button_bar_chat)}
|%s - ${resources.getString(R.string.button_bar_bookmarks)}
|%s - ${resources.getString(R.string.button_bar_shares)}
|%s - ${resources.getString(R.string.button_bar_podcasts)}
""".trimMargin(),
boolToMark(currentServerSetting!!.chatSupport),
boolToMark(currentServerSetting!!.bookmarkSupport),
boolToMark(currentServerSetting!!.shareSupport),
boolToMark(currentServerSetting!!.podcastSupport)
)
}
@Throws(Throwable::class) @Throws(Throwable::class)
override fun doInBackground(): Boolean { override fun doInBackground(): String {
updateProgress(R.string.settings_testing_connection)
currentServerSetting!!.chatSupport = null
currentServerSetting!!.bookmarkSupport = null
currentServerSetting!!.shareSupport = null
currentServerSetting!!.podcastSupport = null
updateProgress(getProgress())
val configuration = SubsonicClientConfiguration( val configuration = SubsonicClientConfiguration(
currentServerSetting!!.url, currentServerSetting!!.url,
currentServerSetting!!.userName, currentServerSetting!!.userName,
@ -330,17 +362,62 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
pingResponse = subsonicApiClient.api.ping().execute() pingResponse = subsonicApiClient.api.ping().execute()
ApiCallResponseChecker.checkResponseSuccessful(pingResponse) ApiCallResponseChecker.checkResponseSuccessful(pingResponse)
currentServerSetting!!.chatSupport = isServerFunctionAvailable {
subsonicApiClient.api.getChatMessages().execute()
}
updateProgress(getProgress())
currentServerSetting!!.bookmarkSupport = isServerFunctionAvailable {
subsonicApiClient.api.getBookmarks().execute()
}
updateProgress(getProgress())
currentServerSetting!!.shareSupport = isServerFunctionAvailable {
subsonicApiClient.api.getShares().execute()
}
updateProgress(getProgress())
currentServerSetting!!.podcastSupport = isServerFunctionAvailable {
subsonicApiClient.api.getPodcasts().execute()
}
updateProgress(getProgress())
val licenseResponse = subsonicApiClient.api.getLicense().execute() val licenseResponse = subsonicApiClient.api.getLicense().execute()
ApiCallResponseChecker.checkResponseSuccessful(licenseResponse) ApiCallResponseChecker.checkResponseSuccessful(licenseResponse)
return licenseResponse.body()!!.license.valid if (!licenseResponse.body()!!.license.valid) {
return getProgress() + "\n" +
resources.getString(R.string.settings_testing_unlicensed)
}
return getProgress()
} }
override fun done(licenseValid: Boolean) { override fun done(responseString: String) {
if (licenseValid) { var dialogText = responseString
Util.toast(activity, R.string.settings_testing_ok) if (arrayOf(
} else { currentServerSetting!!.chatSupport,
Util.toast(activity, R.string.settings_testing_unlicensed) currentServerSetting!!.bookmarkSupport,
currentServerSetting!!.shareSupport,
currentServerSetting!!.podcastSupport
).any { x -> x == false }
) {
dialogText = String.format(
Locale.ROOT,
"%s\n\n%s",
responseString,
resources.getString(R.string.server_editor_disabled_feature)
)
} }
Util.showDialog(
activity,
android.R.drawable.ic_dialog_info,
R.string.settings_testing_ok,
dialogText
)
} }
override fun error(error: Throwable) { override fun error(error: Throwable) {
@ -359,6 +436,18 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
task.execute() task.execute()
} }
private fun isServerFunctionAvailable(function: () -> Response<out SubsonicResponse>): Boolean {
return try {
val response = function()
ApiCallResponseChecker.checkResponseSuccessful(response)
true
} catch (_: IOException) {
false
} catch (_: SubsonicRESTException) {
false
}
}
/** /**
* Finishes the Activity, after confirmation from the user if needed * Finishes the Activity, after confirmation from the user if needed
*/ */

View File

@ -297,7 +297,6 @@
<string name="settings.show_track_number">Zobrazovat číslo skladby</string> <string name="settings.show_track_number">Zobrazovat číslo skladby</string>
<string name="settings.show_track_number_summary">Připojovat číslo skladby při zobrazování skladby</string> <string name="settings.show_track_number_summary">Připojovat číslo skladby při zobrazování skladby</string>
<string name="settings.test_connection_title">Test připojení</string> <string name="settings.test_connection_title">Test připojení</string>
<string name="settings.testing_connection">Testuji připojení&#8230;</string>
<string name="settings.testing_ok">Připojení je v pořádku</string> <string name="settings.testing_ok">Připojení je v pořádku</string>
<string name="settings.testing_unlicensed">Připojení je v pořádku. Server bez licence.</string> <string name="settings.testing_unlicensed">Připojení je v pořádku. Server bez licence.</string>
<string name="settings.theme_light">Světlý</string> <string name="settings.theme_light">Světlý</string>

View File

@ -296,7 +296,6 @@
<string name="settings.show_track_number">Titelnummer anzeigen</string> <string name="settings.show_track_number">Titelnummer anzeigen</string>
<string name="settings.show_track_number_summary">Titel mit Nummer anzeigen</string> <string name="settings.show_track_number_summary">Titel mit Nummer anzeigen</string>
<string name="settings.test_connection_title">Verbindung testen</string> <string name="settings.test_connection_title">Verbindung testen</string>
<string name="settings.testing_connection">Teste Verbindung&#8230;</string>
<string name="settings.testing_ok">Verbindung OK</string> <string name="settings.testing_ok">Verbindung OK</string>
<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>

View File

@ -309,7 +309,6 @@
<string name="settings.show_track_number">Mostrar número de pista</string> <string name="settings.show_track_number">Mostrar número de pista</string>
<string name="settings.show_track_number_summary">Incluir el número de pista cuando se muestre una canción</string> <string name="settings.show_track_number_summary">Incluir el número de pista cuando se muestre una canción</string>
<string name="settings.test_connection_title">Comprobar conexión</string> <string name="settings.test_connection_title">Comprobar conexión</string>
<string name="settings.testing_connection">Comprobado conexión&#8230;</string>
<string name="settings.testing_ok">La conexión es correcta</string> <string name="settings.testing_ok">La conexión es correcta</string>
<string name="settings.testing_unlicensed">La conexión es correcta. Servidor sin licencia.</string> <string name="settings.testing_unlicensed">La conexión es correcta. Servidor sin licencia.</string>
<string name="settings.theme_light">Claro</string> <string name="settings.theme_light">Claro</string>

View File

@ -297,7 +297,6 @@
<string name="settings.show_track_number">Afficher le numéro du titre</string> <string name="settings.show_track_number">Afficher le numéro du titre</string>
<string name="settings.show_track_number_summary">Inclure son numero lors de l\'affichage d\'un titre</string> <string name="settings.show_track_number_summary">Inclure son numero lors de l\'affichage d\'un titre</string>
<string name="settings.test_connection_title">Tester la connexion</string> <string name="settings.test_connection_title">Tester la connexion</string>
<string name="settings.testing_connection">Connexion en cours de test&#8230;</string>
<string name="settings.testing_ok">Connexion correcte</string> <string name="settings.testing_ok">Connexion correcte</string>
<string name="settings.testing_unlicensed">Connexion correcte. Serveur sans licence.</string> <string name="settings.testing_unlicensed">Connexion correcte. Serveur sans licence.</string>
<string name="settings.theme_light">Clair</string> <string name="settings.theme_light">Clair</string>
@ -499,5 +498,6 @@
<string name="feature_flags_five_star_rating_description">Utiliser un système de notation à base d\'étoiles pour les morceaux <string name="feature_flags_five_star_rating_description">Utiliser un système de notation à base d\'étoiles pour les morceaux
au lieu de simplement mettre en avant les morceaux. au lieu de simplement mettre en avant les morceaux.
</string> </string>
<string name="server_editor.disabled_feature">Une ou plusieurs fonctionnalités ont été désactivées car le serveur ne les prend pas en charge.\nVous pouvez réexécuter ce test à tout moment.</string>
</resources> </resources>

View File

@ -297,7 +297,6 @@
<string name="settings.show_track_number">Sorszám megjelenítése</string> <string name="settings.show_track_number">Sorszám megjelenítése</string>
<string name="settings.show_track_number_summary">Dalok sorszámának megjelenítése.</string> <string name="settings.show_track_number_summary">Dalok sorszámának megjelenítése.</string>
<string name="settings.test_connection_title">Kapcsolat tesztelése</string> <string name="settings.test_connection_title">Kapcsolat tesztelése</string>
<string name="settings.testing_connection">Kapcsolat tesztelése&#8230;</string>
<string name="settings.testing_ok">Kapcsolat OK!</string> <string name="settings.testing_ok">Kapcsolat OK!</string>
<string name="settings.testing_unlicensed">Kapcsolat OK! A kiszolgálónak nincs licence!</string> <string name="settings.testing_unlicensed">Kapcsolat OK! A kiszolgálónak nincs licence!</string>
<string name="settings.theme_light">Világos</string> <string name="settings.theme_light">Világos</string>

View File

@ -289,7 +289,6 @@
<string name="settings.show_track_number">Visualizza numero traccia</string> <string name="settings.show_track_number">Visualizza numero traccia</string>
<string name="settings.show_track_number_summary">Includi numero traccia quando visualizzi una canzone</string> <string name="settings.show_track_number_summary">Includi numero traccia quando visualizzi una canzone</string>
<string name="settings.test_connection_title">Prova Connessione</string> <string name="settings.test_connection_title">Prova Connessione</string>
<string name="settings.testing_connection">Collaudo connessione&#8230;</string>
<string name="settings.testing_ok">Connessione OK</string> <string name="settings.testing_ok">Connessione OK</string>
<string name="settings.testing_unlicensed">Connessione OK. Server senza licenza.</string> <string name="settings.testing_unlicensed">Connessione OK. Server senza licenza.</string>
<string name="settings.theme_light">Chiaro</string> <string name="settings.theme_light">Chiaro</string>

View File

@ -307,7 +307,6 @@
<string name="settings.show_track_number">Itemnummer tonen</string> <string name="settings.show_track_number">Itemnummer tonen</string>
<string name="settings.show_track_number_summary">Itemnummer tonen tijdens tonen van nummers</string> <string name="settings.show_track_number_summary">Itemnummer tonen tijdens tonen van nummers</string>
<string name="settings.test_connection_title">Verbinding testen</string> <string name="settings.test_connection_title">Verbinding testen</string>
<string name="settings.testing_connection">Bezig met testen van verbinding&#8230;</string>
<string name="settings.testing_ok">Verbinding is goed</string> <string name="settings.testing_ok">Verbinding is goed</string>
<string name="settings.testing_unlicensed">Verbinding is goed; geen serverlicentie.</string> <string name="settings.testing_unlicensed">Verbinding is goed; geen serverlicentie.</string>
<string name="settings.theme_light">Licht</string> <string name="settings.theme_light">Licht</string>

View File

@ -295,7 +295,6 @@
<string name="settings.show_track_number">Wyświetlaj numer utworu</string> <string name="settings.show_track_number">Wyświetlaj numer utworu</string>
<string name="settings.show_track_number_summary">Dołącza numer utworu podczas wyświetlania utworu</string> <string name="settings.show_track_number_summary">Dołącza numer utworu podczas wyświetlania utworu</string>
<string name="settings.test_connection_title">Testuj połączenie</string> <string name="settings.test_connection_title">Testuj połączenie</string>
<string name="settings.testing_connection">Trwa testowanie połączenia&#8230;</string>
<string name="settings.testing_ok">Połączenie jest OK</string> <string name="settings.testing_ok">Połączenie jest OK</string>
<string name="settings.testing_unlicensed">Połączenie jest OK. Brak licencji na serwerze.</string> <string name="settings.testing_unlicensed">Połączenie jest OK. Brak licencji na serwerze.</string>
<string name="settings.theme_light">Jasny</string> <string name="settings.theme_light">Jasny</string>

View File

@ -297,7 +297,6 @@
<string name="settings.show_track_number">Mostrar o Número da Faixa</string> <string name="settings.show_track_number">Mostrar o Número da Faixa</string>
<string name="settings.show_track_number_summary">Incluir o número da faixa quando mostrando uma música</string> <string name="settings.show_track_number_summary">Incluir o número da faixa quando mostrando uma música</string>
<string name="settings.test_connection_title">Teste de Conexão</string> <string name="settings.test_connection_title">Teste de Conexão</string>
<string name="settings.testing_connection">Testando conexão&#8230;</string>
<string name="settings.testing_ok">Conexão OK</string> <string name="settings.testing_ok">Conexão OK</string>
<string name="settings.testing_unlicensed">Conexão OK. Servidor não licenciado.</string> <string name="settings.testing_unlicensed">Conexão OK. Servidor não licenciado.</string>
<string name="settings.theme_light">Claro</string> <string name="settings.theme_light">Claro</string>

View File

@ -295,7 +295,6 @@
<string name="settings.show_track_number">Mostrar o Número da Faixa</string> <string name="settings.show_track_number">Mostrar o Número da Faixa</string>
<string name="settings.show_track_number_summary">Incluir o número da faixa quando mostrando uma música</string> <string name="settings.show_track_number_summary">Incluir o número da faixa quando mostrando uma música</string>
<string name="settings.test_connection_title">Teste de Conexão</string> <string name="settings.test_connection_title">Teste de Conexão</string>
<string name="settings.testing_connection">Testando conexão&#8230;</string>
<string name="settings.testing_ok">Conexão OK</string> <string name="settings.testing_ok">Conexão OK</string>
<string name="settings.testing_unlicensed">Conexão OK. Servidor não licenciado.</string> <string name="settings.testing_unlicensed">Conexão OK. Servidor não licenciado.</string>
<string name="settings.theme_light">Claro</string> <string name="settings.theme_light">Claro</string>

View File

@ -290,7 +290,6 @@
<string name="settings.show_track_number">Показать номер трека</string> <string name="settings.show_track_number">Показать номер трека</string>
<string name="settings.show_track_number_summary">Включить номер дорожки при отображении песни</string> <string name="settings.show_track_number_summary">Включить номер дорожки при отображении песни</string>
<string name="settings.test_connection_title">Тестовое соединение</string> <string name="settings.test_connection_title">Тестовое соединение</string>
<string name="settings.testing_connection">Тестирование соединения&#8230;</string>
<string name="settings.testing_ok">Успешное соединение</string> <string name="settings.testing_ok">Успешное соединение</string>
<string name="settings.testing_unlicensed">Успешное соединение. Сервер нелицензионный.</string> <string name="settings.testing_unlicensed">Успешное соединение. Сервер нелицензионный.</string>
<string name="settings.theme_light">Светлая</string> <string name="settings.theme_light">Светлая</string>

View File

@ -220,7 +220,6 @@
<string name="settings.show_notification">显示通知</string> <string name="settings.show_notification">显示通知</string>
<string name="settings.show_notification_always">总是显示通知</string> <string name="settings.show_notification_always">总是显示通知</string>
<string name="settings.test_connection_title">测试连接</string> <string name="settings.test_connection_title">测试连接</string>
<string name="settings.testing_connection">测试连接&#8230;</string>
<string name="settings.testing_ok">连接正常</string> <string name="settings.testing_ok">连接正常</string>
<string name="settings.testing_unlicensed">连接正常, 服务器未授权。</string> <string name="settings.testing_unlicensed">连接正常, 服务器未授权。</string>
<string name="settings.theme_title">主题</string> <string name="settings.theme_title">主题</string>

View File

@ -311,7 +311,6 @@
<string name="settings.show_track_number">Show Track Number</string> <string name="settings.show_track_number">Show Track Number</string>
<string name="settings.show_track_number_summary">Include track number when displaying a song</string> <string name="settings.show_track_number_summary">Include track number when displaying a song</string>
<string name="settings.test_connection_title">Test Connection</string> <string name="settings.test_connection_title">Test Connection</string>
<string name="settings.testing_connection">Testing connection&#8230;</string>
<string name="settings.testing_ok">Connection is OK</string> <string name="settings.testing_ok">Connection is OK</string>
<string name="settings.testing_unlicensed">Connection is OK. Server unlicensed.</string> <string name="settings.testing_unlicensed">Connection is OK. Server unlicensed.</string>
<string name="settings.theme_light">Light</string> <string name="settings.theme_light">Light</string>
@ -462,6 +461,7 @@
<string name="server_menu.move_down">Move down</string> <string name="server_menu.move_down">Move down</string>
<string name="server_editor.authentication">Authentication</string> <string name="server_editor.authentication">Authentication</string>
<string name="server_editor.advanced">Advanced settings</string> <string name="server_editor.advanced">Advanced settings</string>
<string name="server_editor.disabled_feature">One or more features were disabled because the server doesn\'t support them.\nYou can run this test again anytime.</string>
<plurals name="select_album_n_songs"> <plurals name="select_album_n_songs">
<item quantity="one">1 song</item> <item quantity="one">1 song</item>