mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-12 15:37:17 +03:00
Retrieve server features in parallel, retrieve Jukebox capabilities as server feature.
This commit is contained in:
parent
2fafebee42
commit
7dea620e66
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,7 +1,7 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Reformat" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<inspection_tool class="Reformat" enabled="false" level="WEAK WARNING" enabled_by_default="false">
|
||||
<option name="processChangedFilesOnly" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
|
@ -54,6 +54,9 @@ interface SubsonicAPIDefinition {
|
||||
@GET("ping.view")
|
||||
fun ping(): Call<SubsonicResponse>
|
||||
|
||||
@GET("ping.view")
|
||||
suspend fun pingSuspend(): SubsonicResponse
|
||||
|
||||
@GET("getLicense.view")
|
||||
fun getLicense(): Call<LicenseResponse>
|
||||
|
||||
@ -164,6 +167,12 @@ interface SubsonicAPIDefinition {
|
||||
@Query("id") id: String? = null
|
||||
): Call<GetPodcastsResponse>
|
||||
|
||||
@GET("getPodcasts.view")
|
||||
suspend fun getPodcastsSuspend(
|
||||
@Query("includeEpisodes") includeEpisodes: Boolean? = null,
|
||||
@Query("id") id: String? = null
|
||||
): GetPodcastsResponse
|
||||
|
||||
@GET("getLyrics.view")
|
||||
fun getLyrics(
|
||||
@Query("artist") artist: String? = null,
|
||||
@ -261,6 +270,9 @@ interface SubsonicAPIDefinition {
|
||||
@GET("getShares.view")
|
||||
fun getShares(): Call<SharesResponse>
|
||||
|
||||
@GET("getShares.view")
|
||||
suspend fun getSharesSuspend(): SharesResponse
|
||||
|
||||
@GET("createShare.view")
|
||||
fun createShare(
|
||||
@Query("id") idsToShare: List<String>,
|
||||
@ -292,15 +304,24 @@ interface SubsonicAPIDefinition {
|
||||
@GET("getUser.view")
|
||||
fun getUser(@Query("username") username: String): Call<GetUserResponse>
|
||||
|
||||
@GET("getUser.view")
|
||||
suspend fun getUserSuspend(@Query("username") username: String): GetUserResponse
|
||||
|
||||
@GET("getChatMessages.view")
|
||||
fun getChatMessages(@Query("since") since: Long? = null): Call<ChatMessagesResponse>
|
||||
|
||||
@GET("getChatMessages.view")
|
||||
suspend fun getChatMessagesSuspend(@Query("since") since: Long? = null): ChatMessagesResponse
|
||||
|
||||
@GET("addChatMessage.view")
|
||||
fun addChatMessage(@Query("message") message: String): Call<SubsonicResponse>
|
||||
|
||||
@GET("getBookmarks.view")
|
||||
fun getBookmarks(): Call<BookmarksResponse>
|
||||
|
||||
@GET("getBookmarks.view")
|
||||
suspend fun getBookmarksSuspend(): BookmarksResponse
|
||||
|
||||
@GET("createBookmark.view")
|
||||
fun createBookmark(
|
||||
@Query("id") id: String,
|
||||
@ -314,6 +335,9 @@ interface SubsonicAPIDefinition {
|
||||
@GET("getVideos.view")
|
||||
fun getVideos(): Call<VideosResponse>
|
||||
|
||||
@GET("getVideos.view")
|
||||
suspend fun getVideosSuspend(): VideosResponse
|
||||
|
||||
@GET("getAvatar.view")
|
||||
fun getAvatar(@Query("username") username: String): Call<ResponseBody>
|
||||
}
|
||||
|
@ -66,6 +66,8 @@ style:
|
||||
active: false
|
||||
ReturnCount:
|
||||
max: 5
|
||||
ForbiddenImport:
|
||||
imports: ['android.app.AlertDialog']
|
||||
|
||||
comments:
|
||||
active: true
|
||||
|
136
ultrasonic/schemas/org.moire.ultrasonic.data.AppDatabase/6.json
Normal file
136
ultrasonic/schemas/org.moire.ultrasonic.data.AppDatabase/6.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 6,
|
||||
"identityHash": "9d28146ad3086d9c761f25ca007a96ce",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "ServerSetting",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `index` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `color` INTEGER, `userName` TEXT NOT NULL, `password` TEXT NOT NULL, `jukeboxByDefault` INTEGER NOT NULL, `allowSelfSignedCertificate` INTEGER NOT NULL, `forcePlainTextPassword` INTEGER NOT NULL, `musicFolderId` TEXT, `minimumApiVersion` TEXT, `chatSupport` INTEGER, `bookmarkSupport` INTEGER, `shareSupport` INTEGER, `podcastSupport` INTEGER, `jukeboxSupport` INTEGER, `videoSupport` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "index",
|
||||
"columnName": "index",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "userName",
|
||||
"columnName": "userName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "password",
|
||||
"columnName": "password",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "jukeboxByDefault",
|
||||
"columnName": "jukeboxByDefault",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "allowSelfSignedCertificate",
|
||||
"columnName": "allowSelfSignedCertificate",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "forcePlainTextPassword",
|
||||
"columnName": "forcePlainTextPassword",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "musicFolderId",
|
||||
"columnName": "musicFolderId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "minimumApiVersion",
|
||||
"columnName": "minimumApiVersion",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatSupport",
|
||||
"columnName": "chatSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookmarkSupport",
|
||||
"columnName": "bookmarkSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "shareSupport",
|
||||
"columnName": "shareSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "podcastSupport",
|
||||
"columnName": "podcastSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "jukeboxSupport",
|
||||
"columnName": "jukeboxSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "videoSupport",
|
||||
"columnName": "videoSupport",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9d28146ad3086d9c761f25ca007a96ce')"
|
||||
]
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@ import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -19,10 +19,8 @@
|
||||
package org.moire.ultrasonic.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import org.moire.ultrasonic.R;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
@ -47,9 +45,9 @@ public abstract class ModalBackgroundTask<T> extends BackgroundTask<T>
|
||||
this(activity, true);
|
||||
}
|
||||
|
||||
private AlertDialog createProgressDialog()
|
||||
private androidx.appcompat.app.AlertDialog createProgressDialog()
|
||||
{
|
||||
AlertDialog.Builder builder = new InfoDialog.Builder(getActivity());
|
||||
InfoDialog.Builder builder = new InfoDialog.Builder(getActivity().getApplicationContext());
|
||||
builder.setTitle(R.string.background_task_wait);
|
||||
builder.setMessage(R.string.background_task_loading);
|
||||
builder.setOnCancelListener(dialogInterface -> cancel());
|
||||
|
@ -172,9 +172,6 @@ class NavigationActivity : AppCompatActivity() {
|
||||
} else {
|
||||
if (!nowPlayingHidden) showNowPlaying()
|
||||
}
|
||||
|
||||
// Hides menu items for Offline mode
|
||||
setMenuForServerCapabilities()
|
||||
}
|
||||
|
||||
// Determine if this is a first run
|
||||
@ -207,6 +204,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
|
||||
rxBusSubscription += RxBus.activeServerChangedObservable.subscribe {
|
||||
updateNavigationHeaderForServer()
|
||||
setMenuForServerCapabilities()
|
||||
}
|
||||
|
||||
serverRepository.liveServerCount().observe(this) { count ->
|
||||
@ -506,6 +504,6 @@ class NavigationActivity : AppCompatActivity() {
|
||||
podcastsMenuItem?.isVisible = activeServer.podcastSupport != false
|
||||
playlistsMenuItem?.isVisible = isOnline
|
||||
downloadsMenuItem?.isVisible = isOnline
|
||||
videoMenuItem?.isVisible = isOnline
|
||||
videoMenuItem?.isVisible = activeServer.videoSupport != false
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +214,9 @@ class ActiveServerProvider(
|
||||
bookmarkSupport = false,
|
||||
podcastSupport = false,
|
||||
shareSupport = false,
|
||||
chatSupport = false
|
||||
chatSupport = false,
|
||||
videoSupport = false,
|
||||
jukeboxSupport = false
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
*/
|
||||
@Database(
|
||||
entities = [ServerSetting::class],
|
||||
version = 5,
|
||||
version = 6,
|
||||
exportSchema = true
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
@ -265,7 +265,6 @@ val MIGRATION_5_4: Migration = object : Migration(5, 4) {
|
||||
database.execSQL("ALTER TABLE `_new_ServerSetting` RENAME TO `ServerSetting`")
|
||||
}
|
||||
}
|
||||
|
||||
/* ktlint-disable max-line-length */
|
||||
val MIGRATION_5_6: Migration = object : Migration(5, 6) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
|
@ -17,8 +17,6 @@ import androidx.room.PrimaryKey
|
||||
* @param forcePlainTextPassword: True if the server authenticates the user using old Ldap-like way
|
||||
* @param musicFolderId: The Id of the MusicFolder to be used with the server
|
||||
*
|
||||
* TODO: forcePlainTextPassword is still using the old column name.
|
||||
* Could be updated on the next significant change to the DB scheme
|
||||
*/
|
||||
@Entity
|
||||
data class ServerSetting(
|
||||
@ -32,14 +30,17 @@ data class ServerSetting(
|
||||
@ColumnInfo(name = "password") var password: String,
|
||||
@ColumnInfo(name = "jukeboxByDefault") var jukeboxByDefault: Boolean,
|
||||
@ColumnInfo(name = "allowSelfSignedCertificate") var allowSelfSignedCertificate: Boolean,
|
||||
@ColumnInfo(name = "ldapSupport") var forcePlainTextPassword: Boolean,
|
||||
@ColumnInfo(name = "forcePlainTextPassword") var forcePlainTextPassword: Boolean,
|
||||
@ColumnInfo(name = "musicFolderId") var musicFolderId: 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
|
||||
@ColumnInfo(name = "podcastSupport") var podcastSupport: Boolean? = null,
|
||||
@ColumnInfo(name = "jukeboxSupport") var jukeboxSupport: Boolean? = null,
|
||||
@ColumnInfo(name = "videoSupport") var videoSupport: Boolean? = null
|
||||
) {
|
||||
|
||||
constructor() : this (
|
||||
0, 0, "", "", null, "", "", false, false, false, null, null
|
||||
)
|
||||
|
@ -13,8 +13,11 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||
@ -23,32 +26,25 @@ 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.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.moire.ultrasonic.BuildConfig
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicRESTException
|
||||
import org.moire.ultrasonic.api.subsonic.falseOnFailure
|
||||
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
|
||||
import org.moire.ultrasonic.api.subsonic.throwOnFailure
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.ServerSetting
|
||||
import org.moire.ultrasonic.model.EditServerModel
|
||||
import org.moire.ultrasonic.model.ServerSettingsModel
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.CommunicationError.getErrorMessage
|
||||
import org.moire.ultrasonic.util.ErrorDialog
|
||||
import org.moire.ultrasonic.util.InfoDialog
|
||||
import org.moire.ultrasonic.util.ModalBackgroundTask
|
||||
import org.moire.ultrasonic.util.ServerColor
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import retrofit2.Response
|
||||
import timber.log.Timber
|
||||
|
||||
private const val DIALOG_PADDING = 12
|
||||
@ -78,6 +74,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
|
||||
private var selectedColor: Int? = null
|
||||
|
||||
private val navArgs by navArgs<EditServerFragmentArgs>()
|
||||
val model: EditServerModel by viewModels()
|
||||
|
||||
@Override
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -372,149 +369,82 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
|
||||
/**
|
||||
* Tests if the network connection to the entered Server Settings can be made
|
||||
*/
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
private fun testConnection() {
|
||||
val task: ModalBackgroundTask<String> = object : ModalBackgroundTask<String>(
|
||||
activity,
|
||||
false
|
||||
) {
|
||||
fun boolToMark(value: Boolean?): String {
|
||||
if (value == null)
|
||||
return "⌛"
|
||||
return if (value) "✔️" else "❌"
|
||||
}
|
||||
val testSetting = ServerSetting()
|
||||
val builder = InfoDialog.Builder(requireContext())
|
||||
builder.setTitle(R.string.supported_server_features)
|
||||
builder.setMessage(getProgress(testSetting))
|
||||
val dialog: AlertDialog = builder.create()
|
||||
dialog.show()
|
||||
|
||||
fun getProgress(): String {
|
||||
return String.format(
|
||||
"""
|
||||
val testJob = lifecycleScope.launch {
|
||||
try {
|
||||
val flow = model.queryFeatureSupport(currentServerSetting!!).flowOn(Dispatchers.IO)
|
||||
|
||||
flow.collect {
|
||||
model.storeFeatureSupport(testSetting, it)
|
||||
dialog.setMessage(getProgress(testSetting))
|
||||
Timber.w("${it.type} support: ${it.supported}")
|
||||
}
|
||||
|
||||
currentServerSetting!!.chatSupport = testSetting.chatSupport
|
||||
currentServerSetting!!.bookmarkSupport = testSetting.bookmarkSupport
|
||||
currentServerSetting!!.shareSupport = testSetting.shareSupport
|
||||
currentServerSetting!!.podcastSupport = testSetting.podcastSupport
|
||||
currentServerSetting!!.videoSupport = testSetting.videoSupport
|
||||
currentServerSetting!!.jukeboxSupport = testSetting.jukeboxSupport
|
||||
} catch (cancellationException: CancellationException) {
|
||||
Timber.i(cancellationException)
|
||||
} catch (exception: Exception) {
|
||||
dialog.dismiss()
|
||||
Timber.w(exception)
|
||||
ErrorDialog.Builder(requireContext())
|
||||
.setTitle(R.string.error_label)
|
||||
.setMessage(getErrorMessage(exception, context))
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
dialog.setOnDismissListener { testJob.cancel() }
|
||||
}
|
||||
|
||||
private fun getProgress(serverSetting: ServerSetting): String {
|
||||
val isAnyDisabled = arrayOf(
|
||||
serverSetting.chatSupport,
|
||||
serverSetting.bookmarkSupport,
|
||||
serverSetting.shareSupport,
|
||||
serverSetting.podcastSupport,
|
||||
serverSetting.videoSupport,
|
||||
serverSetting.jukeboxSupport,
|
||||
).any { x -> x == false }
|
||||
|
||||
var progressString = 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)}
|
||||
|%s - ${resources.getString(R.string.main_videos)}
|
||||
|%s - ${resources.getString(R.string.jukebox)}
|
||||
""".trimMargin(),
|
||||
boolToMark(currentServerSetting!!.chatSupport),
|
||||
boolToMark(currentServerSetting!!.bookmarkSupport),
|
||||
boolToMark(currentServerSetting!!.shareSupport),
|
||||
boolToMark(currentServerSetting!!.podcastSupport)
|
||||
)
|
||||
}
|
||||
boolToMark(serverSetting.chatSupport),
|
||||
boolToMark(serverSetting.bookmarkSupport),
|
||||
boolToMark(serverSetting.shareSupport),
|
||||
boolToMark(serverSetting.podcastSupport),
|
||||
boolToMark(serverSetting.videoSupport),
|
||||
boolToMark(serverSetting.jukeboxSupport)
|
||||
)
|
||||
if (isAnyDisabled)
|
||||
progressString += "\n\n" + resources.getString(R.string.server_editor_disabled_feature)
|
||||
|
||||
@Throws(Throwable::class)
|
||||
override fun doInBackground(): String {
|
||||
|
||||
currentServerSetting!!.chatSupport = null
|
||||
currentServerSetting!!.bookmarkSupport = null
|
||||
currentServerSetting!!.shareSupport = null
|
||||
currentServerSetting!!.podcastSupport = null
|
||||
|
||||
updateProgress(getProgress())
|
||||
|
||||
val configuration = SubsonicClientConfiguration(
|
||||
currentServerSetting!!.url,
|
||||
currentServerSetting!!.userName,
|
||||
currentServerSetting!!.password,
|
||||
SubsonicAPIVersions.getClosestKnownClientApiVersion(
|
||||
Constants.REST_PROTOCOL_VERSION
|
||||
),
|
||||
Constants.REST_CLIENT_ID,
|
||||
currentServerSetting!!.allowSelfSignedCertificate,
|
||||
currentServerSetting!!.forcePlainTextPassword,
|
||||
BuildConfig.DEBUG
|
||||
)
|
||||
val subsonicApiClient = SubsonicAPIClient(configuration)
|
||||
|
||||
// Execute a ping to retrieve the API version.
|
||||
// This is accepted to fail if the authentication is incorrect yet.
|
||||
var pingResponse = subsonicApiClient.api.ping().execute()
|
||||
if (pingResponse.body() != null) {
|
||||
val restApiVersion = pingResponse.body()!!.version.restApiVersion
|
||||
currentServerSetting!!.minimumApiVersion = restApiVersion
|
||||
Timber.i("Server minimum API version set to %s", restApiVersion)
|
||||
}
|
||||
|
||||
// Execute a ping to check the authentication, now using the correct API version.
|
||||
pingResponse = subsonicApiClient.api.ping().execute()
|
||||
pingResponse.throwOnFailure()
|
||||
|
||||
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()
|
||||
licenseResponse.throwOnFailure()
|
||||
|
||||
if (!licenseResponse.body()!!.license.valid) {
|
||||
return getProgress() + "\n" +
|
||||
resources.getString(R.string.settings_testing_unlicensed)
|
||||
}
|
||||
return getProgress()
|
||||
}
|
||||
|
||||
override fun done(responseString: String) {
|
||||
var dialogText = responseString
|
||||
if (arrayOf(
|
||||
currentServerSetting!!.chatSupport,
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
InfoDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.settings_testing_ok)
|
||||
.setMessage(dialogText)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun error(error: Throwable) {
|
||||
Timber.w(error)
|
||||
ErrorDialog(
|
||||
context = activity,
|
||||
message = String.format(
|
||||
"%s %s",
|
||||
resources.getString(R.string.settings_connection_failure),
|
||||
getErrorMessage(error)
|
||||
)
|
||||
).show()
|
||||
}
|
||||
}
|
||||
task.execute()
|
||||
return progressString
|
||||
}
|
||||
|
||||
private fun isServerFunctionAvailable(function: () -> Response<out SubsonicResponse>): Boolean {
|
||||
return try {
|
||||
function().falseOnFailure()
|
||||
} catch (_: IOException) {
|
||||
false
|
||||
} catch (_: SubsonicRESTException) {
|
||||
false
|
||||
}
|
||||
private fun boolToMark(value: Boolean?): String {
|
||||
if (value == null)
|
||||
return "⌛"
|
||||
return if (value) "✔️" else "❌"
|
||||
}
|
||||
|
||||
/**
|
||||
@ -522,7 +452,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
|
||||
*/
|
||||
private fun finishActivity() {
|
||||
if (areFieldsChanged()) {
|
||||
ErrorDialog.Builder(context)
|
||||
ErrorDialog.Builder(requireContext())
|
||||
.setTitle(R.string.common_confirm)
|
||||
.setMessage(R.string.server_editor_leave_confirmation)
|
||||
.setPositiveButton(R.string.common_ok) { dialog, _ ->
|
||||
|
@ -8,7 +8,6 @@
|
||||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color.argb
|
||||
import android.graphics.Point
|
||||
@ -90,6 +89,7 @@ import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
|
||||
import org.moire.ultrasonic.subsonic.ShareHandler
|
||||
import org.moire.ultrasonic.util.CancellationToken
|
||||
import org.moire.ultrasonic.util.CommunicationError
|
||||
import org.moire.ultrasonic.util.ConfirmationDialog
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import org.moire.ultrasonic.util.toTrack
|
||||
@ -1256,12 +1256,14 @@ class PlayerFragment :
|
||||
mediaPlayerController.setSongRating(rating)
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
private fun showSavePlaylistDialog() {
|
||||
val layout = LayoutInflater.from(this.context).inflate(R.layout.save_playlist, null)
|
||||
val layout = LayoutInflater.from(this.context)
|
||||
.inflate(R.layout.save_playlist, null)
|
||||
|
||||
playlistNameView = layout.findViewById(R.id.save_playlist_name)
|
||||
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
|
||||
val builder = ConfirmationDialog.Builder(requireContext())
|
||||
builder.setTitle(R.string.download_playlist_title)
|
||||
builder.setMessage(R.string.download_playlist_name)
|
||||
|
||||
|
@ -88,7 +88,8 @@ class ServerSelectorFragment : Fragment() {
|
||||
* This Callback handles the deletion of a Server Setting
|
||||
*/
|
||||
private fun deleteServerById(id: Int) {
|
||||
ErrorDialog.Builder(context)
|
||||
// FIXME
|
||||
ErrorDialog.Builder(requireContext())
|
||||
.setTitle(R.string.server_menu_delete)
|
||||
.setMessage(R.string.server_selector_delete_confirmation)
|
||||
.setPositiveButton(R.string.common_delete) { dialog, _ ->
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
@ -34,6 +33,7 @@ import org.moire.ultrasonic.log.FileLoggerTree.Companion.uprootFromTimberForest
|
||||
import org.moire.ultrasonic.provider.SearchSuggestionProvider
|
||||
import org.moire.ultrasonic.service.MediaPlayerController
|
||||
import org.moire.ultrasonic.service.RxBus
|
||||
import org.moire.ultrasonic.util.ConfirmationDialog
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.ErrorDialog
|
||||
import org.moire.ultrasonic.util.FileUtil.ultrasonicDirectory
|
||||
@ -136,7 +136,7 @@ class SettingsFragment :
|
||||
return
|
||||
}
|
||||
}
|
||||
ErrorDialog.Builder(context)
|
||||
ErrorDialog.Builder(requireContext())
|
||||
.setMessage(R.string.settings_cache_location_error)
|
||||
.show()
|
||||
}
|
||||
@ -297,7 +297,7 @@ class SettingsFragment :
|
||||
onChosen: (Int) -> Unit
|
||||
) {
|
||||
val choice = intArrayOf(defaultChoice)
|
||||
AlertDialog.Builder(activity).setTitle(title)
|
||||
ConfirmationDialog.Builder(requireContext()).setTitle(title)
|
||||
.setSingleChoiceItems(
|
||||
R.array.bluetoothDeviceSettingNames, defaultChoice
|
||||
) { _: DialogInterface?, i: Int -> choice[0] = i }
|
||||
@ -404,7 +404,7 @@ class SettingsFragment :
|
||||
)
|
||||
val keep = R.string.settings_debug_log_keep
|
||||
val delete = R.string.settings_debug_log_delete
|
||||
InfoDialog.Builder(activity)
|
||||
ConfirmationDialog.Builder(requireContext())
|
||||
.setMessage(message)
|
||||
.setNegativeButton(keep) { dIf: DialogInterface, _: Int ->
|
||||
dIf.cancel()
|
||||
@ -413,7 +413,7 @@ class SettingsFragment :
|
||||
deleteLogFiles()
|
||||
Timber.i("Deleted debug log files")
|
||||
dIf.dismiss()
|
||||
AlertDialog.Builder(activity)
|
||||
InfoDialog.Builder(requireContext())
|
||||
.setMessage(R.string.settings_debug_log_deleted)
|
||||
.setPositiveButton(R.string.common_ok) { dIf2: DialogInterface, _: Int ->
|
||||
dIf2.dismiss()
|
||||
|
@ -229,7 +229,7 @@ open class TrackCollectionFragment(
|
||||
|
||||
unpinButton?.setOnClickListener {
|
||||
if (Settings.showConfirmationDialog) {
|
||||
ConfirmationDialog.Builder(context)
|
||||
ConfirmationDialog.Builder(requireContext())
|
||||
.setMessage(R.string.common_unpin_selection_confirmation)
|
||||
.setPositiveButton(R.string.common_unpin) { _, _ ->
|
||||
unpin()
|
||||
@ -245,7 +245,7 @@ open class TrackCollectionFragment(
|
||||
|
||||
deleteButton?.setOnClickListener {
|
||||
if (Settings.showConfirmationDialog) {
|
||||
ConfirmationDialog.Builder(context)
|
||||
ConfirmationDialog.Builder(requireContext())
|
||||
.setMessage(R.string.common_delete_selection_confirmation)
|
||||
.setPositiveButton(R.string.common_delete) { _, _ ->
|
||||
delete()
|
||||
|
@ -8,7 +8,6 @@
|
||||
package org.moire.ultrasonic.fragment.legacy
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
@ -43,7 +42,9 @@ import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||
import org.moire.ultrasonic.util.BackgroundTask
|
||||
import org.moire.ultrasonic.util.CacheCleaner
|
||||
import org.moire.ultrasonic.util.CancellationToken
|
||||
import org.moire.ultrasonic.util.ConfirmationDialog
|
||||
import org.moire.ultrasonic.util.FragmentBackgroundTask
|
||||
import org.moire.ultrasonic.util.InfoDialog
|
||||
import org.moire.ultrasonic.util.LoadingTask
|
||||
import org.moire.ultrasonic.util.Util.applyTheme
|
||||
import org.moire.ultrasonic.util.Util.toast
|
||||
@ -222,7 +223,7 @@ class PlaylistsFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun deletePlaylist(playlist: Playlist) {
|
||||
AlertDialog.Builder(context).setIcon(R.drawable.ic_baseline_warning)
|
||||
ConfirmationDialog.Builder(requireContext()).setIcon(R.drawable.ic_baseline_warning)
|
||||
.setTitle(R.string.common_confirm).setMessage(
|
||||
resources.getString(R.string.delete_playlist, playlist.name)
|
||||
).setPositiveButton(R.string.common_ok) { _, _ ->
|
||||
@ -283,8 +284,8 @@ class PlaylistsFragment : Fragment() {
|
||||
Linkify.addLinks(message, Linkify.WEB_URLS)
|
||||
textView.text = message
|
||||
textView.movementMethod = LinkMovementMethod.getInstance()
|
||||
AlertDialog.Builder(context).setTitle(playlist.name).setCancelable(true)
|
||||
.setIcon(R.drawable.ic_baseline_info).setView(textView).show()
|
||||
InfoDialog.Builder(requireContext()).setTitle(playlist.name).setCancelable(true)
|
||||
.setView(textView).show()
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@ -301,7 +302,7 @@ class PlaylistsFragment : Fragment() {
|
||||
} else {
|
||||
publicBox.isChecked = pub
|
||||
}
|
||||
val alertDialog = AlertDialog.Builder(context)
|
||||
val alertDialog = ConfirmationDialog.Builder(requireContext())
|
||||
alertDialog.setIcon(R.drawable.ic_baseline_warning)
|
||||
alertDialog.setTitle(R.string.playlist_update_info)
|
||||
alertDialog.setView(dialogView)
|
||||
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* EditServerModel.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.model
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.BuildConfig
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIDefinition
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicRESTException
|
||||
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.ServerSetting
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import retrofit2.HttpException
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class EditServerModel(val app: Application) : AndroidViewModel(app), KoinComponent {
|
||||
|
||||
val activeServerProvider: ActiveServerProvider by inject()
|
||||
|
||||
private suspend fun serverFunctionAvailable(
|
||||
type: ServerFeature,
|
||||
function: suspend () -> SubsonicResponse
|
||||
): FeatureSupport {
|
||||
val result = try {
|
||||
function().falseOnFailure()
|
||||
} catch (_: IOException) {
|
||||
false
|
||||
} catch (_: SubsonicRESTException) {
|
||||
false
|
||||
} catch (_: HttpException) {
|
||||
false
|
||||
}
|
||||
return FeatureSupport(type, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension checks API call results for errors, API version, etc
|
||||
* @return Boolean: True if everything was ok, false if an error was found
|
||||
*/
|
||||
private fun SubsonicResponse.falseOnFailure(): Boolean {
|
||||
return (this.status === SubsonicResponse.Status.OK)
|
||||
}
|
||||
|
||||
private fun requestFlow(
|
||||
type: ServerFeature,
|
||||
api: SubsonicAPIDefinition,
|
||||
userName: String
|
||||
) = flow {
|
||||
when (type) {
|
||||
ServerFeature.CHAT -> emit(
|
||||
serverFunctionAvailable(type, api::getChatMessagesSuspend)
|
||||
)
|
||||
ServerFeature.BOOKMARK -> emit(
|
||||
serverFunctionAvailable(type, api::getBookmarksSuspend)
|
||||
)
|
||||
ServerFeature.SHARE -> emit(
|
||||
serverFunctionAvailable(type, api::getSharesSuspend)
|
||||
)
|
||||
ServerFeature.PODCAST -> emit(
|
||||
serverFunctionAvailable(type, api::getPodcastsSuspend)
|
||||
)
|
||||
ServerFeature.JUKEBOX -> emit(
|
||||
serverFunctionAvailable(type) {
|
||||
val response = api.getUserSuspend(userName)
|
||||
if (!response.user.jukeboxRole) throw IOException()
|
||||
response
|
||||
}
|
||||
)
|
||||
ServerFeature.VIDEO -> emit(
|
||||
serverFunctionAvailable(type, api::getVideosSuspend)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
suspend fun queryFeatureSupport(currentServerSetting: ServerSetting): Flow<FeatureSupport> {
|
||||
val client = buildTestClient(currentServerSetting)
|
||||
// One line of magic:
|
||||
// Get all possible feature values, turn them into a flow,
|
||||
// and execute each request concurrently
|
||||
return (ServerFeature.values()).asFlow().flatMapMerge {
|
||||
requestFlow(it, client.api, currentServerSetting.userName)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun buildTestClient(serverSetting: ServerSetting): SubsonicAPIClient {
|
||||
val configuration = SubsonicClientConfiguration(
|
||||
serverSetting.url,
|
||||
serverSetting.userName,
|
||||
serverSetting.password,
|
||||
SubsonicAPIVersions.getClosestKnownClientApiVersion(
|
||||
Constants.REST_PROTOCOL_VERSION
|
||||
),
|
||||
Constants.REST_CLIENT_ID,
|
||||
serverSetting.allowSelfSignedCertificate,
|
||||
serverSetting.forcePlainTextPassword,
|
||||
BuildConfig.DEBUG
|
||||
)
|
||||
|
||||
val client = SubsonicAPIClient(configuration)
|
||||
|
||||
// Execute a ping to retrieve the API version.
|
||||
// This is accepted to fail if the authentication is incorrect yet.
|
||||
var pingResponse = client.api.pingSuspend()
|
||||
val restApiVersion = pingResponse.version.restApiVersion
|
||||
serverSetting.minimumApiVersion = restApiVersion
|
||||
Timber.i("Server minimum API version set to %s", restApiVersion)
|
||||
|
||||
// Execute a ping to check the authentication, now using the correct API version.
|
||||
pingResponse = client.api.pingSuspend()
|
||||
return client
|
||||
}
|
||||
|
||||
fun storeFeatureSupport(settings: ServerSetting, it: FeatureSupport) {
|
||||
when (it.type) {
|
||||
ServerFeature.CHAT -> settings.chatSupport = it.supported
|
||||
ServerFeature.BOOKMARK -> settings.bookmarkSupport = it.supported
|
||||
ServerFeature.SHARE -> settings.shareSupport = it.supported
|
||||
ServerFeature.PODCAST -> settings.podcastSupport = it.supported
|
||||
ServerFeature.JUKEBOX -> settings.jukeboxSupport = it.supported
|
||||
ServerFeature.VIDEO -> settings.videoSupport = it.supported
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
enum class ServerFeature(val named: String) {
|
||||
CHAT("chat"),
|
||||
BOOKMARK("bookmark"),
|
||||
SHARE("share"),
|
||||
PODCAST("podcast"),
|
||||
JUKEBOX("jukebox"),
|
||||
VIDEO("video")
|
||||
}
|
||||
|
||||
data class FeatureSupport(val type: ServerFeature, val supported: Boolean)
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
package org.moire.ultrasonic.subsonic
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
@ -27,6 +27,7 @@ import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.util.BackgroundTask
|
||||
import org.moire.ultrasonic.util.CancellationToken
|
||||
import org.moire.ultrasonic.util.ConfirmationDialog
|
||||
import org.moire.ultrasonic.util.FragmentBackgroundTask
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.ShareDetails
|
||||
@ -150,6 +151,8 @@ class ShareHandler(val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@SuppressLint("InflateParams")
|
||||
private fun showDialog(
|
||||
fragment: Fragment,
|
||||
shareDetails: ShareDetails,
|
||||
@ -184,7 +187,7 @@ class ShareHandler(val context: Context) {
|
||||
}
|
||||
updateVisibility()
|
||||
|
||||
val builder = AlertDialog.Builder(fragment.context)
|
||||
val builder = ConfirmationDialog.Builder(fragment.requireContext())
|
||||
builder.setTitle(R.string.share_set_share_options)
|
||||
|
||||
builder.setPositiveButton(R.string.menu_share) { _, _ ->
|
||||
|
@ -8,10 +8,14 @@
|
||||
package org.moire.ultrasonic.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.moire.ultrasonic.R
|
||||
|
||||
/*
|
||||
* InfoDialog can be used to show some information to the user. Typically it cannot be cancelled,
|
||||
* only dismissed via OK.
|
||||
*/
|
||||
open class InfoDialog(
|
||||
context: Context,
|
||||
message: CharSequence?,
|
||||
@ -19,7 +23,7 @@ open class InfoDialog(
|
||||
private val finishActivityOnClose: Boolean = false
|
||||
) {
|
||||
|
||||
open var builder: AlertDialog.Builder = Builder(activity ?: context, message)
|
||||
open var builder: MaterialAlertDialogBuilder = Builder(activity ?: context, message)
|
||||
|
||||
fun show() {
|
||||
builder.setOnCancelListener {
|
||||
@ -35,7 +39,7 @@ open class InfoDialog(
|
||||
builder.create().show()
|
||||
}
|
||||
|
||||
class Builder(context: Context?) : AlertDialog.Builder(context) {
|
||||
class Builder(context: Context) : MaterialAlertDialogBuilder(context) {
|
||||
|
||||
constructor(context: Context, message: CharSequence?) : this(context) {
|
||||
setMessage(message)
|
||||
@ -44,7 +48,6 @@ open class InfoDialog(
|
||||
init {
|
||||
setIcon(R.drawable.ic_baseline_info)
|
||||
setTitle(R.string.common_confirm)
|
||||
setCancelable(true)
|
||||
setPositiveButton(R.string.common_ok) { _, _ ->
|
||||
// Just close it
|
||||
}
|
||||
@ -52,6 +55,10 @@ open class InfoDialog(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ErrorDialog can be used to show some an error to the user.
|
||||
* Typically it cannot be cancelled, only dismissed via OK.
|
||||
*/
|
||||
class ErrorDialog(
|
||||
context: Context,
|
||||
message: CharSequence?,
|
||||
@ -59,9 +66,9 @@ class ErrorDialog(
|
||||
finishActivityOnClose: Boolean = false
|
||||
) : InfoDialog(context, message, activity, finishActivityOnClose) {
|
||||
|
||||
override var builder: AlertDialog.Builder = Builder(activity ?: context, message)
|
||||
override var builder: MaterialAlertDialogBuilder = Builder(activity ?: context, message)
|
||||
|
||||
class Builder(context: Context?) : AlertDialog.Builder(context) {
|
||||
class Builder(context: Context) : MaterialAlertDialogBuilder(context) {
|
||||
constructor(context: Context, message: CharSequence?) : this(context) {
|
||||
setMessage(message)
|
||||
}
|
||||
@ -69,7 +76,6 @@ class ErrorDialog(
|
||||
init {
|
||||
setIcon(R.drawable.ic_baseline_warning)
|
||||
setTitle(R.string.error_label)
|
||||
setCancelable(true)
|
||||
setPositiveButton(R.string.common_ok) { _, _ ->
|
||||
// Just close it
|
||||
}
|
||||
@ -77,6 +83,10 @@ class ErrorDialog(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ConfirmationDialog can be used to present a choice to the user.
|
||||
* Typically it will be cancelable..
|
||||
*/
|
||||
class ConfirmationDialog(
|
||||
context: Context,
|
||||
message: CharSequence?,
|
||||
@ -84,9 +94,9 @@ class ConfirmationDialog(
|
||||
finishActivityOnClose: Boolean = false
|
||||
) : InfoDialog(context, message, activity, finishActivityOnClose) {
|
||||
|
||||
override var builder: AlertDialog.Builder = Builder(activity ?: context, message)
|
||||
override var builder: MaterialAlertDialogBuilder = Builder(activity ?: context, message)
|
||||
|
||||
class Builder(context: Context?) : AlertDialog.Builder(context) {
|
||||
class Builder(context: Context) : MaterialAlertDialogBuilder(context) {
|
||||
constructor(context: Context, message: CharSequence?) : this(context) {
|
||||
setMessage(message)
|
||||
}
|
||||
|
@ -157,7 +157,6 @@
|
||||
<string name="settings.clear_bookmark">Zahodit záložku</string>
|
||||
<string name="settings.clear_bookmark_summary">Zahodit záložku po dokončení přehrávání skladby</string>
|
||||
<string name="settings.clear_search_history">Vyčistit historii vyhledávání</string>
|
||||
<string name="settings.connection_failure">Chyba připojení.</string>
|
||||
<string name="settings.default_albums">Výchozí alba</string>
|
||||
<string name="settings.default_artists">Výchozí umělci</string>
|
||||
<string name="settings.default_songs">Výchozí skladby</string>
|
||||
@ -248,8 +247,6 @@
|
||||
<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.test_connection_title">Test připojení</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.theme_light">Světlý</string>
|
||||
<string name="settings.theme_dark">Tmavý</string>
|
||||
<string name="settings.theme_black">Černý</string>
|
||||
|
@ -196,7 +196,6 @@
|
||||
<string name="settings.clear_bookmark">Lesezeichen löschen</string>
|
||||
<string name="settings.clear_bookmark_summary">Lesezeichen nach Wiedergabe löschen</string>
|
||||
<string name="settings.clear_search_history">Suchverlauf löschen</string>
|
||||
<string name="settings.connection_failure">Verbindungsfehler</string>
|
||||
<string name="settings.default_albums">Anzahl der Alben</string>
|
||||
<string name="settings.default_artists">Anzahl der Künstler*innen</string>
|
||||
<string name="settings.default_songs">Anzahl der Titel</string>
|
||||
@ -297,8 +296,6 @@
|
||||
<string name="settings.show_track_number">Titelnummer 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.testing_ok">Verbindung OK</string>
|
||||
<string name="settings.testing_unlicensed">Verbindung OK, Server nicht lizenziert.</string>
|
||||
<string name="settings.theme_light">Hell</string>
|
||||
<string name="settings.theme_dark">Dunkel</string>
|
||||
<string name="settings.theme_black">Schwarz</string>
|
||||
|
@ -198,7 +198,6 @@
|
||||
<string name="settings.clear_bookmark">Limpiar marcador</string>
|
||||
<string name="settings.clear_bookmark_summary">Limpiar marcador tras la finalización de la reproducción de una canción</string>
|
||||
<string name="settings.clear_search_history">Limpiar el historial de búsqueda</string>
|
||||
<string name="settings.connection_failure">Fallo de conexión.</string>
|
||||
<string name="settings.default_albums">Álbumes predeterminados</string>
|
||||
<string name="settings.default_artists">Artistas predeterminados</string>
|
||||
<string name="settings.default_songs">Canciones predeterminadas</string>
|
||||
@ -299,8 +298,6 @@
|
||||
<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.test_connection_title">Comprobar conexión</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.theme_day_night">Día y noche</string>
|
||||
<string name="settings.theme_light">Claro</string>
|
||||
<string name="settings.theme_dark">Oscuro</string>
|
||||
|
@ -194,7 +194,6 @@
|
||||
<string name="settings.clear_bookmark">Effacer le signet</string>
|
||||
<string name="settings.clear_bookmark_summary">Effacer le signet à la fin de la lecture d\'un titre</string>
|
||||
<string name="settings.clear_search_history">Effacer l\'historique des recherches</string>
|
||||
<string name="settings.connection_failure">Échec de la connexion</string>
|
||||
<string name="settings.default_albums">Albums par défaut</string>
|
||||
<string name="settings.default_artists">Artistes par défaut</string>
|
||||
<string name="settings.default_songs">Musiques par défaut</string>
|
||||
@ -291,8 +290,6 @@
|
||||
<string name="settings.show_track_number">Afficher le numéro du titre</string>
|
||||
<string name="settings.show_track_number_summary">Inclure son numéro lors de l\'affichage d\'un titre</string>
|
||||
<string name="settings.test_connection_title">Tester la connexion</string>
|
||||
<string name="settings.testing_ok">Connexion correcte</string>
|
||||
<string name="settings.testing_unlicensed">Connexion correcte. Serveur sans licence.</string>
|
||||
<string name="settings.theme_light">Clair</string>
|
||||
<string name="settings.theme_dark">Sombre</string>
|
||||
<string name="settings.theme_black">Noir</string>
|
||||
|
@ -163,7 +163,6 @@
|
||||
<string name="settings.clear_bookmark">Könyvjelző törlése</string>
|
||||
<string name="settings.clear_bookmark_summary">Könyvjelző törlése a dal lejátszása után.</string>
|
||||
<string name="settings.clear_search_history">Keresési előzmények törlése</string>
|
||||
<string name="settings.connection_failure">Csatlakozási hiba!</string>
|
||||
<string name="settings.default_albums">Albumok találati száma</string>
|
||||
<string name="settings.default_artists">Előadók találati száma</string>
|
||||
<string name="settings.default_songs">Dalok találati száma</string>
|
||||
@ -256,8 +255,6 @@
|
||||
<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.test_connection_title">Kapcsolat tesztelése</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.theme_light">Világos</string>
|
||||
<string name="settings.theme_dark">Sötét</string>
|
||||
<string name="settings.theme_black">Fekete</string>
|
||||
|
@ -154,7 +154,6 @@
|
||||
<string name="settings.clear_bookmark">Pulisci Segnalibro</string>
|
||||
<string name="settings.clear_bookmark_summary">Pulisci segnalibro al completamento della riproduzione di una canzone</string>
|
||||
<string name="settings.clear_search_history">Pulisci Storico Ricerca</string>
|
||||
<string name="settings.connection_failure">Errore connessione.</string>
|
||||
<string name="settings.default_albums">Album predefiniti</string>
|
||||
<string name="settings.default_artists">Artisti predefiniti</string>
|
||||
<string name="settings.default_songs">Canzoni predefinte</string>
|
||||
@ -240,8 +239,6 @@
|
||||
<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.test_connection_title">Prova Connessione</string>
|
||||
<string name="settings.testing_ok">Connessione OK</string>
|
||||
<string name="settings.testing_unlicensed">Connessione OK. Server senza licenza.</string>
|
||||
<string name="settings.theme_light">Chiaro</string>
|
||||
<string name="settings.theme_dark">Scuro</string>
|
||||
<string name="settings.theme_title">Tema</string>
|
||||
|
@ -198,7 +198,6 @@
|
||||
<string name="settings.clear_bookmark">Bladwijzer verwijderen</string>
|
||||
<string name="settings.clear_bookmark_summary">Bladwijzer verwijderen nadat nummer is afgespeeld</string>
|
||||
<string name="settings.clear_search_history">Zoekgeschiedenis wissen</string>
|
||||
<string name="settings.connection_failure">Verbindingsfout.</string>
|
||||
<string name="settings.default_albums">Standaardalbums</string>
|
||||
<string name="settings.default_artists">Standaardartiesten</string>
|
||||
<string name="settings.default_songs">Standaardnummers</string>
|
||||
@ -299,8 +298,6 @@
|
||||
<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.test_connection_title">Verbinding testen</string>
|
||||
<string name="settings.testing_ok">Verbinding is goed</string>
|
||||
<string name="settings.testing_unlicensed">Verbinding is goed; geen serverlicentie.</string>
|
||||
<string name="settings.theme_day_night">Dag en nacht</string>
|
||||
<string name="settings.theme_light">Licht</string>
|
||||
<string name="settings.theme_dark">Donker</string>
|
||||
|
@ -157,7 +157,6 @@
|
||||
<string name="settings.clear_bookmark">Czyszczenie zakładek</string>
|
||||
<string name="settings.clear_bookmark_summary">Czyść zakładkę po zakończeniu odtwarzania utworu</string>
|
||||
<string name="settings.clear_search_history">Wyczyść historię wyszukiwania</string>
|
||||
<string name="settings.connection_failure">Błąd połączenia.</string>
|
||||
<string name="settings.default_albums">Domyślna ilość wyników - albumy</string>
|
||||
<string name="settings.default_artists">Domyślna ilość wyników - artyści</string>
|
||||
<string name="settings.default_songs">Domyślna ilość wyników - utwory</string>
|
||||
@ -248,8 +247,6 @@
|
||||
<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.test_connection_title">Testuj połączenie</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.theme_light">Jasny</string>
|
||||
<string name="settings.theme_dark">Ciemny</string>
|
||||
<string name="settings.theme_title">Motyw</string>
|
||||
|
@ -196,7 +196,6 @@
|
||||
<string name="settings.clear_bookmark">Limpar Favoritos</string>
|
||||
<string name="settings.clear_bookmark_summary">Limpar favoritos após terminar de tocar a música</string>
|
||||
<string name="settings.clear_search_history">Limpar Histórico de Pesquisas</string>
|
||||
<string name="settings.connection_failure">Falha na conexão.</string>
|
||||
<string name="settings.default_albums">Álbuns Padrões</string>
|
||||
<string name="settings.default_artists">Artistas Padrões</string>
|
||||
<string name="settings.default_songs">Músicas Padrões</string>
|
||||
@ -297,8 +296,6 @@
|
||||
<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 ao mostrar uma música</string>
|
||||
<string name="settings.test_connection_title">Teste de Conexão</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.theme_day_night">Dia & Noite</string>
|
||||
<string name="settings.theme_light">Claro</string>
|
||||
<string name="settings.theme_dark">Escuro</string>
|
||||
|
@ -157,7 +157,6 @@
|
||||
<string name="settings.clear_bookmark">Limpar Favoritos</string>
|
||||
<string name="settings.clear_bookmark_summary">Limpar favoritos após terminar de tocar a música</string>
|
||||
<string name="settings.clear_search_history">Limpar Histórico de Pesquisas</string>
|
||||
<string name="settings.connection_failure">Falha na conexão.</string>
|
||||
<string name="settings.default_albums">Álbuns Padrões</string>
|
||||
<string name="settings.default_artists">Artistas Padrões</string>
|
||||
<string name="settings.default_songs">Músicas Padrões</string>
|
||||
@ -248,8 +247,6 @@
|
||||
<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.test_connection_title">Teste de Conexão</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.theme_light">Claro</string>
|
||||
<string name="settings.theme_dark">Escuro</string>
|
||||
<string name="settings.theme_title">Tema</string>
|
||||
|
@ -181,7 +181,6 @@
|
||||
<string name="settings.clear_bookmark">Очистить закладку</string>
|
||||
<string name="settings.clear_bookmark_summary">Очистить закладку после завершения воспроизведения песни</string>
|
||||
<string name="settings.clear_search_history">Очистить историю поиска</string>
|
||||
<string name="settings.connection_failure">Ошибка подключения.</string>
|
||||
<string name="settings.default_albums">Альбомы по умолчанию</string>
|
||||
<string name="settings.default_artists">Исполнители по умолчанию</string>
|
||||
<string name="settings.default_songs">Треки по умолчанию</string>
|
||||
@ -274,8 +273,6 @@
|
||||
<string name="settings.show_track_number">Показать номер трека</string>
|
||||
<string name="settings.show_track_number_summary">Включить номер дорожки при отображении песни</string>
|
||||
<string name="settings.test_connection_title">Тестовое соединение</string>
|
||||
<string name="settings.testing_ok">Успешное соединение</string>
|
||||
<string name="settings.testing_unlicensed">Успешное соединение. Сервер нелицензионный.</string>
|
||||
<string name="settings.theme_light">Светлая</string>
|
||||
<string name="settings.theme_dark">Темная</string>
|
||||
<string name="settings.theme_black">Черная</string>
|
||||
|
@ -181,7 +181,6 @@
|
||||
<string name="settings.clear_bookmark">清空书签</string>
|
||||
<string name="settings.clear_bookmark_summary">歌曲播放完毕后清除书签</string>
|
||||
<string name="settings.clear_search_history">清空搜索历史</string>
|
||||
<string name="settings.connection_failure">连接失败</string>
|
||||
<string name="settings.default_albums">默认专辑</string>
|
||||
<string name="settings.default_artists">默认艺术家</string>
|
||||
<string name="settings.default_songs">默认音乐</string>
|
||||
@ -277,8 +276,6 @@
|
||||
<string name="settings.show_track_number">显示曲目编号</string>
|
||||
<string name="settings.show_track_number_summary">显示歌曲时包括曲目编号</string>
|
||||
<string name="settings.test_connection_title">测试连接</string>
|
||||
<string name="settings.testing_ok">连接正常</string>
|
||||
<string name="settings.testing_unlicensed">连接正常, 服务器未授权。</string>
|
||||
<string name="settings.theme_light">Light</string>
|
||||
<string name="settings.theme_dark">Dark</string>
|
||||
<string name="settings.theme_black">Black</string>
|
||||
|
@ -198,7 +198,6 @@
|
||||
<string name="settings.clear_bookmark">Clear Bookmark</string>
|
||||
<string name="settings.clear_bookmark_summary">Clear bookmark upon completion of playback of a song</string>
|
||||
<string name="settings.clear_search_history">Clear Search History</string>
|
||||
<string name="settings.connection_failure">Connection failure.</string>
|
||||
<string name="settings.default_albums">Default Albums</string>
|
||||
<string name="settings.default_artists">Default Artists</string>
|
||||
<string name="settings.default_songs">Default Songs</string>
|
||||
@ -299,8 +298,6 @@
|
||||
<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.test_connection_title">Test Connection</string>
|
||||
<string name="settings.testing_ok">Connection is OK</string>
|
||||
<string name="settings.testing_unlicensed">Connection is OK. Server unlicensed.</string>
|
||||
<string name="settings.theme_day_night">Day & Night</string>
|
||||
<string name="settings.theme_light">Light</string>
|
||||
<string name="settings.theme_dark">Dark</string>
|
||||
@ -446,11 +443,12 @@
|
||||
<!-- Subsonic features -->
|
||||
<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>
|
||||
|
||||
<string name="settings.use_hw_offload_title">Use hardware playback (experimental)</string>
|
||||
<string name="settings.use_hw_offload_description">Try to play the media using the media decoder chip on your phone. This can improve battery usage.</string>
|
||||
<string name="list_view">List</string>
|
||||
<string name="grid_view">Cover</string>
|
||||
<string name="supported_server_features">Supported features</string>
|
||||
<string name="jukebox">Jukebox</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user