mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-07-19 18:01:58 +03:00
Compare commits
32 Commits
e284cd1fa1
...
4a5f7d67a0
Author | SHA1 | Date | |
---|---|---|---|
|
4a5f7d67a0 | ||
|
9be6c8d371 | ||
|
5a4989186e | ||
|
eb380b9af9 | ||
|
01124c8ecf | ||
|
6a97636c7a | ||
|
53ea17d2b9 | ||
|
a1e339f850 | ||
|
4809317c63 | ||
|
c5c0497716 | ||
|
79ac73020b | ||
|
d9dfef4016 | ||
|
3bd3607220 | ||
|
e35a33edde | ||
|
c1013f6b80 | ||
|
21a27c691d | ||
|
25f3ff0bd3 | ||
|
4feb84bd83 | ||
|
4c049671db | ||
|
3a1251dd2a | ||
|
8dd7758bc6 | ||
|
296308cebf | ||
|
45ca0966fd | ||
|
77d3f8c11b | ||
|
7a453dbd30 | ||
|
0a6a12c70a | ||
|
448fdb70b0 | ||
|
5e4ec56ae7 | ||
|
8c42700676 | ||
|
22fda501f4 | ||
|
556d3bb90d | ||
|
4e9cea87a8 |
@ -74,7 +74,9 @@ Unit Tests:
|
||||
|
||||
Assemble Release:
|
||||
stage: Build
|
||||
script: ./gradlew assembleRelease
|
||||
script:
|
||||
- sed -i 's/applicationId \"org.moire.ultrasonic\"/applicationId "org.moire.ultrasonic.gitlab"/' ultrasonic/build.gradle
|
||||
- ./gradlew assembleRelease
|
||||
artifacts:
|
||||
name: ultrasonic-release-unsigned-${CI_COMMIT_SHA}
|
||||
paths:
|
||||
|
10
.gitlab/merge_request_templates/Release.md
Normal file
10
.gitlab/merge_request_templates/Release.md
Normal file
@ -0,0 +1,10 @@
|
||||
#### Before merge:
|
||||
- [ ] MR is targetting the master branch
|
||||
- [ ] **Squash commits must be disabled!**
|
||||
- [ ] RoboTests (5 physical, 10 virtual) on a Release apk return no errors
|
||||
- [ ] Release notes present
|
||||
|
||||
#### After merge
|
||||
- [ ] ``git fetch``
|
||||
- [ ] Create an annotated and signed tag: ``git tag -sa``
|
||||
- [ ] Push the tag to git:``git push --tags``
|
@ -52,7 +52,11 @@ style:
|
||||
active: true
|
||||
ForbiddenComment:
|
||||
active: true
|
||||
values: ['FIXME:', 'STOPSHIP:']
|
||||
comments:
|
||||
- reason: 'Forbidden FIXME todo marker in comment, please fix the problem.'
|
||||
value: 'FIXME:'
|
||||
- reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.'
|
||||
value: 'STOPSHIP:'
|
||||
WildcardImport:
|
||||
active: true
|
||||
MaxLineLength:
|
||||
|
@ -21,5 +21,5 @@ android.nonFinalResIds=true
|
||||
org.gradle.unsafe.configuration-cache=true
|
||||
|
||||
# TODO Renable on day (check that Retrofit, Jackson, and Imageloader are working)
|
||||
android.enableR8.fullMode=false
|
||||
android.enableR8.fullMode=true
|
||||
|
||||
|
@ -3,11 +3,11 @@
|
||||
gradle = "8.1.1"
|
||||
|
||||
navigation = "2.5.3"
|
||||
gradlePlugin = "8.0.1"
|
||||
gradlePlugin = "8.0.2"
|
||||
androidxcore = "1.10.1"
|
||||
ktlint = "0.43.2"
|
||||
ktlintGradle = "11.3.2"
|
||||
detekt = "1.22.0"
|
||||
detekt = "1.23.0"
|
||||
preferences = "1.2.0"
|
||||
media3 = "1.0.2"
|
||||
|
||||
@ -31,7 +31,7 @@ picasso = "2.8"
|
||||
junit4 = "4.13.2"
|
||||
junit5 = "5.9.3"
|
||||
mockito = "5.3.1"
|
||||
mockitoKotlin = "4.1.0"
|
||||
mockitoKotlin = "5.0.0"
|
||||
kluent = "1.73"
|
||||
apacheCodecs = "1.15"
|
||||
robolectric = "4.10.3"
|
||||
|
@ -14,7 +14,7 @@ android {
|
||||
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
resConfigs 'cs', 'de', 'en', 'es', 'fr', 'gl', 'hu', 'it', 'ja', 'nb-rNO', 'nl', 'pl', 'pt', 'pt-rBR', 'ru', 'zh-rCN', 'zh-rTW'
|
||||
resourceConfigurations += ['cs', 'de', 'en', 'es', 'fr', 'gl', 'hu', 'it', 'ja', 'nb-rNO', 'nl', 'pl', 'pt', 'pt-rBR', 'ru', 'zh-rCN', 'zh-rTW']
|
||||
}
|
||||
|
||||
bundle.language.enableSplit = false
|
||||
|
@ -1,5 +1,4 @@
|
||||
#### From Jackson
|
||||
|
||||
-keepattributes *Annotation*,EnclosingMethod,Signature
|
||||
-keepnames class com.fasterxml.jackson.** {
|
||||
*;
|
||||
|
@ -1,8 +1,14 @@
|
||||
-dontobfuscate
|
||||
|
||||
### Don't remove subsonic api serializers/entities
|
||||
-keep class org.moire.ultrasonic.api.subsonic.response.** { *; }
|
||||
-keep class org.moire.ultrasonic.api.subsonic.models.** { *; }
|
||||
-keep class org.moire.ultrasonic.api.subsonic.** { *; }
|
||||
|
||||
## Don't remove the domain models
|
||||
-keep class org.moire.ultrasonic.domain.** { *; }
|
||||
|
||||
## Don't remove the imageloader
|
||||
-keep class org.moire.ultrasonic.imageloader.** { *; }
|
||||
-keep class org.moire.ultrasonic.provider.AlbumArtContentProvider { *; }
|
||||
|
||||
## Don't remove NowPlayingFragment
|
||||
-keep class org.moire.ultrasonic.fragment.NowPlayingFragment { *; }
|
||||
|
@ -58,7 +58,7 @@ public abstract class BackgroundTask<T> implements ProgressListener
|
||||
|
||||
protected String getErrorMessage(Throwable error)
|
||||
{
|
||||
return CommunicationError.getErrorMessage(error, activity);
|
||||
return CommunicationError.getErrorMessage(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -286,7 +286,8 @@ class NavigationActivity : AppCompatActivity() {
|
||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
||||
private fun setupAppShortcut() {
|
||||
|
||||
// TODO: Handle adding shortcut only if player is ready to add songs (has any servers setup)
|
||||
if (UApp.instance!!.isFirstRun)
|
||||
return
|
||||
val shortcutIntent = Intent(this, NavigationActivity::class.java).apply {
|
||||
action = Constants.INTENT_PLAY_RANDOM_SONGS
|
||||
}
|
||||
@ -398,7 +399,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
if (intent == null) return
|
||||
|
||||
if (intent.action == Constants.INTENT_PLAY_RANDOM_SONGS) {
|
||||
val currentFragment = host!!.childFragmentManager.fragments.last()
|
||||
val currentFragment = host?.childFragmentManager?.fragments?.last() ?: return
|
||||
val service = MusicServiceFactory.getMusicService()
|
||||
val musicDirectory = service.getRandomSongs(Settings.maxSongs)
|
||||
val downloadHandler: DownloadHandler by inject()
|
||||
|
@ -142,8 +142,9 @@ class TrackViewHolder(val view: View) :
|
||||
|
||||
// Listen for rating updates
|
||||
rxBusSubscription!! += RxBus.ratingPublishedObservable.subscribe {
|
||||
launch(Dispatchers.Main) {
|
||||
// Ignore updates which are not for the current song
|
||||
if (it.id != song.id) return@subscribe
|
||||
if (it.id != song.id) return@launch
|
||||
|
||||
if (it.rating is HeartRating) {
|
||||
updateSingleStar(it.rating.isHeart)
|
||||
@ -152,6 +153,7 @@ class TrackViewHolder(val view: View) :
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is called when the Holder is recycled and receives a new Song
|
||||
fun dispose() {
|
||||
|
@ -401,7 +401,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
|
||||
Timber.w(exception)
|
||||
ErrorDialog.Builder(requireContext())
|
||||
.setTitle(R.string.error_label)
|
||||
.setMessage(getErrorMessage(exception, context))
|
||||
.setMessage(getErrorMessage(exception))
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.BaseAdapter
|
||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.audiofx.EqualizerController
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.shouldUseId3Tags
|
||||
@ -662,7 +663,6 @@ class PlayerFragment :
|
||||
parentId = track.parent,
|
||||
isAlbum = true
|
||||
)
|
||||
|
||||
findNavController().navigate(action)
|
||||
return true
|
||||
}
|
||||
@ -822,16 +822,16 @@ class PlayerFragment :
|
||||
musicService.createPlaylist(null, playlistName, entries)
|
||||
}.invokeOnCompletion {
|
||||
if (it == null || it is CancellationException) {
|
||||
Util.toast(context, R.string.download_playlist_done)
|
||||
Util.toast(UApp.applicationContext(), R.string.download_playlist_done)
|
||||
} else {
|
||||
Timber.e(it, "Exception has occurred in savePlaylistInBackground")
|
||||
val msg = String.format(
|
||||
Locale.ROOT,
|
||||
"%s %s",
|
||||
resources.getString(R.string.download_playlist_error),
|
||||
CommunicationError.getErrorMessage(it, context)
|
||||
CommunicationError.getErrorMessage(it)
|
||||
)
|
||||
Util.toast(context, msg)
|
||||
Util.toast(UApp.applicationContext(), msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -351,13 +351,11 @@ open class TrackCollectionFragment(
|
||||
|
||||
val isArtist = navArgs.isArtist
|
||||
|
||||
// Need a valid id to download stuff
|
||||
val id = navArgs.id ?: return
|
||||
|
||||
if (hasSubFolders) {
|
||||
// Need a valid id to recurse sub directories stuff
|
||||
if (hasSubFolders && navArgs.id != null) {
|
||||
downloadHandler.fetchTracksAndAddToController(
|
||||
fragment = this,
|
||||
id = id,
|
||||
id = navArgs.id!!,
|
||||
append = append,
|
||||
autoPlay = !append,
|
||||
shuffle = shuffle,
|
||||
|
@ -505,7 +505,7 @@ class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player {
|
||||
listeners.queueEvent(Player.EVENT_MEDIA_ITEM_TRANSITION) {
|
||||
it.onMediaItemTransition(
|
||||
currentMedia,
|
||||
Player.MEDIA_ITEM_TRANSITION_REASON_SEEK
|
||||
Player.MEDIA_ITEM_TRANSITION_REASON_AUTO
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,6 @@ data class PlaybackState(
|
||||
var repeatMode: Int = 0
|
||||
) : Serializable {
|
||||
companion object {
|
||||
const val serialVersionUID = -293487987L
|
||||
private const val serialVersionUID = -293487987L
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ class DownloadHandler(
|
||||
var successString: String? = null
|
||||
|
||||
// Launch the Job
|
||||
executeTaskWithToast(fragment, {
|
||||
executeTaskWithToast({
|
||||
val tracksToDownload: List<Track> = tracks
|
||||
?: getTracksFromServer(isArtist, id!!, isDirectory, name, isShare)
|
||||
|
||||
@ -104,7 +104,7 @@ class DownloadHandler(
|
||||
) {
|
||||
var successString: String? = null
|
||||
// Launch the Job
|
||||
executeTaskWithToast(fragment, {
|
||||
executeTaskWithToast({
|
||||
val songs: MutableList<Track> =
|
||||
getTracksFromServer(isArtist, id, isDirectory, name, isShare)
|
||||
|
||||
|
@ -20,6 +20,7 @@ import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicRESTException
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.subsonic.getLocalizedErrorMessage
|
||||
import timber.log.Timber
|
||||
|
||||
@ -46,14 +47,14 @@ object CommunicationError {
|
||||
|
||||
ErrorDialog(
|
||||
context = context,
|
||||
message = getErrorMessage(error, context)
|
||||
message = getErrorMessage(error)
|
||||
).show()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("ReturnCount")
|
||||
fun getErrorMessage(error: Throwable, context: Context?): String {
|
||||
if (context == null) return "Couldn't get Error message, Context is null"
|
||||
fun getErrorMessage(error: Throwable): String {
|
||||
val context = UApp.applicationContext()
|
||||
if (error is IOException && !Util.hasUsableNetwork()) {
|
||||
return context.resources.getString(R.string.background_task_no_network)
|
||||
} else if (error is FileNotFoundException) {
|
||||
|
@ -17,6 +17,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import timber.log.Timber
|
||||
|
||||
object CoroutinePatterns {
|
||||
@ -30,7 +31,6 @@ object CoroutinePatterns {
|
||||
}
|
||||
|
||||
fun CoroutineScope.executeTaskWithToast(
|
||||
fragment: Fragment,
|
||||
task: suspend CoroutineScope.() -> Unit,
|
||||
successString: () -> String?
|
||||
): Job {
|
||||
@ -40,7 +40,7 @@ fun CoroutineScope.executeTaskWithToast(
|
||||
// Setup a handler when the job is done
|
||||
job.invokeOnCompletion {
|
||||
val toastString = if (it != null && it !is CancellationException) {
|
||||
CommunicationError.getErrorMessage(it, fragment.context)
|
||||
CommunicationError.getErrorMessage(it)
|
||||
} else {
|
||||
successString()
|
||||
}
|
||||
@ -49,7 +49,7 @@ fun CoroutineScope.executeTaskWithToast(
|
||||
if (toastString == null) return@invokeOnCompletion
|
||||
|
||||
launch(Dispatchers.Main) {
|
||||
Util.toast(fragment.context, toastString)
|
||||
Util.toast(UApp.applicationContext(), toastString)
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ fun CoroutineScope.executeTaskWithModalDialog(
|
||||
successString: () -> String
|
||||
) {
|
||||
// Create the job
|
||||
val job = executeTaskWithToast(fragment, task, successString)
|
||||
val job = executeTaskWithToast(task, successString)
|
||||
|
||||
// Create the dialog
|
||||
val builder = InfoDialog.Builder(fragment.requireContext())
|
||||
|
@ -7,7 +7,7 @@
|
||||
android:orientation="horizontal"
|
||||
android:padding="6dp" >
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_select"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -20,7 +20,7 @@
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_play_now"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -31,9 +31,10 @@
|
||||
android:contentDescription="@string/common.play_now"
|
||||
app:icon="@drawable/media_start"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_play_next"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -44,9 +45,10 @@
|
||||
android:contentDescription="@string/common.play_next"
|
||||
app:icon="@drawable/ic_play_next"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_play_last"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -57,9 +59,11 @@
|
||||
android:contentDescription="@string/common.play_last"
|
||||
app:icon="@drawable/ic_play_last"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none"
|
||||
/>
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_pin"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -70,9 +74,11 @@
|
||||
android:contentDescription="@string/common.pin"
|
||||
app:icon="@drawable/ic_menu_pin"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none"
|
||||
/>
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_unpin"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -83,9 +89,11 @@
|
||||
android:contentDescription="@string/common.unpin"
|
||||
app:icon="@drawable/ic_menu_unpin"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none"
|
||||
/>
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_download"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -96,9 +104,11 @@
|
||||
android:contentDescription="@string/common.download"
|
||||
app:icon="@drawable/ic_menu_download"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none"
|
||||
/>
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_delete"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -109,9 +119,11 @@
|
||||
android:contentDescription="@string/common.delete"
|
||||
app:icon="@drawable/ic_menu_close"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none"
|
||||
/>
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/select_album_more"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="0dp"
|
||||
@ -122,6 +134,8 @@
|
||||
android:contentDescription="@string/search.more"
|
||||
app:icon="@drawable/media_forward"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconSize="26dp" />
|
||||
app:iconSize="26dp"
|
||||
android:scrollbars="none"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -452,4 +452,5 @@
|
||||
<string name="supported_server_features">Funciones soportadas</string>
|
||||
<string name="foreground_exception_title">No se puede reanudar la reproducción</string>
|
||||
<string name="foreground_exception_text">Presione el botón de reproducción en la notificación de medios si aún está presente; de lo contrario, abra la aplicación para iniciar la reproducción y vuelva a conectar la sesión al controlador</string>
|
||||
<string name="settings.max_bitrate_pinning">Tasa de bits máxima: al fijar una canción de forma permanente</string>
|
||||
</resources>
|
@ -67,7 +67,7 @@
|
||||
<string name="download.menu_save">保存播放列表</string>
|
||||
<string name="download.menu_screen_off">关闭屏幕常亮</string>
|
||||
<string name="download.menu_screen_on">开启屏幕常亮</string>
|
||||
<string name="download.menu_show_album">显示专辑</string>
|
||||
<string name="download.menu_show_album">转到专辑</string>
|
||||
<string name="download.menu_shuffle">随机</string>
|
||||
<string name="download.menu_shuffle_on">随机播放模式已启用</string>
|
||||
<string name="download.menu_shuffle_off">随机播放模式已禁用</string>
|
||||
@ -280,7 +280,7 @@
|
||||
<string name="settings.test_connection_title">测试连接</string>
|
||||
<string name="settings.theme_light">亮色</string>
|
||||
<string name="settings.theme_dark">暗色</string>
|
||||
<string name="settings.theme_black">Black</string>
|
||||
<string name="settings.theme_black">黑色</string>
|
||||
<string name="settings.theme_title">主题</string>
|
||||
<string name="settings.title.allow_self_signed_certificate">允许自签名 HTTPS 证书</string>
|
||||
<string name="settings.title.force_plain_text_password">强制原始密码认证</string>
|
||||
@ -337,7 +337,7 @@
|
||||
<string name="share_default_greeting">看看我从 %s 分享的这首音乐</string>
|
||||
<string name="share_via">分享歌曲通过</string>
|
||||
<string name="menu.share">分享</string>
|
||||
<string name="download.menu_show_artist">显示艺术家</string>
|
||||
<string name="download.menu_show_artist">转到艺术家</string>
|
||||
<string name="common_multiple_years">数年</string>
|
||||
<string name="settings.debug.title">调试选项</string>
|
||||
<string name="settings.debug.log_to_file">将调试日志写入文件</string>
|
||||
@ -448,4 +448,5 @@
|
||||
<string name="language.cs">捷克语</string>
|
||||
<string name="language.de">德语</string>
|
||||
<string name="language.pt_BR">葡萄牙语(巴西)</string>
|
||||
<string name="settings.max_bitrate_pinning">最大比特率 - 永久固定歌曲时</string>
|
||||
</resources>
|
@ -84,7 +84,7 @@
|
||||
<string name="settings.increment_time_8">8 秒</string>
|
||||
<string name="settings.custom_cache_location">使用自訂緩衝路徑</string>
|
||||
<string name="settings.cache_location">緩衝路径</string>
|
||||
<string name="settings.cache_location_error">錯誤緩衝路徑,使用預設緩衝路徑</string>
|
||||
<string name="settings.cache_location_error">錯誤緩衝路徑,使用預設緩衝路徑。</string>
|
||||
<string name="settings.cache_size">緩衝大小</string>
|
||||
<string name="settings.cache_size_100">100 MB</string>
|
||||
<string name="settings.cache_size_1000">1 GB</string>
|
||||
@ -129,7 +129,7 @@
|
||||
<string name="time_span_disabled">已停用</string>
|
||||
<string name="share_comment">註記</string>
|
||||
<string name="server_menu.delete">刪除</string>
|
||||
<string name="download.menu_show_album">顯示專輯</string>
|
||||
<string name="download.menu_show_album">轉至專輯</string>
|
||||
<string name="language.zh_CN">簡體中文(中國)</string>
|
||||
<string name="download.menu_save">儲存播放清單</string>
|
||||
<string name="download.bookmark_set_at_position" formatted="false">書籤設置在 %s。</string>
|
||||
@ -232,4 +232,68 @@
|
||||
<string name="settings.hide_media_toast">在 Android 系統下次掃描裝置內音樂時生效。</string>
|
||||
<string name="settings.download_transition">播放時顯示正在播放介面</string>
|
||||
<string name="settings.download_transition_summary">在媒體庫介面開始播放後切換到正在播放介面</string>
|
||||
<string name="settings.search_50">50</string>
|
||||
<string name="settings.preload_3">3 首歌</string>
|
||||
<string name="settings.search_1">1</string>
|
||||
<string name="settings.search_20">20</string>
|
||||
<string name="settings.search_75">75</string>
|
||||
<string name="settings.share_minutes">分鐘</string>
|
||||
<string name="settings.preload_500">500 首歌</string>
|
||||
<string name="settings.max_bitrate_112">112 Kbps</string>
|
||||
<string name="settings.preload_1">1 首歌</string>
|
||||
<string name="settings.search_3">3</string>
|
||||
<string name="settings.search_40">40</string>
|
||||
<string name="settings.search_500">500</string>
|
||||
<string name="settings.max_bitrate_160">160 Kbps</string>
|
||||
<string name="util.zero_time">0:00</string>
|
||||
<string name="settings.network_timeout_120000">120 秒</string>
|
||||
<string name="settings.share_hours">小時</string>
|
||||
<string name="settings.theme_black">黑色</string>
|
||||
<string name="server_editor.authentication">認證</string>
|
||||
<string name="settings.preload_2">2 首歌</string>
|
||||
<string name="settings.search_10">10</string>
|
||||
<string name="settings.server_address">伺服器地址</string>
|
||||
<string name="settings.network_timeout_60000">60 秒</string>
|
||||
<string name="settings.search_5">5</string>
|
||||
<string name="settings.preload_100">100 首歌</string>
|
||||
<string name="settings.theme_light">明色</string>
|
||||
<string name="settings.max_bitrate_96">96 Kbps</string>
|
||||
<string name="util.bytes_format.kilobyte">0 KB</string>
|
||||
<string name="settings.override_language">覆寫當前語言</string>
|
||||
<string name="settings.preload_5">5 首歌</string>
|
||||
<string name="settings.search_250">250</string>
|
||||
<string name="settings.max_bitrate_192">192 Kbps</string>
|
||||
<string name="settings.max_bitrate_80">80 Kbps</string>
|
||||
<string name="settings.search_25">25</string>
|
||||
<string name="settings.search_30">30</string>
|
||||
<string name="settings.search_100">100</string>
|
||||
<string name="main.video" tools:ignore="UnusedResources">影片</string>
|
||||
<string name="time_span_disable">禁用</string>
|
||||
<string name="settings.preload_50">50 首歌</string>
|
||||
<string name="song_details.kbps">%d kbps</string>
|
||||
<string name="settings.preload_10">10 首歌</string>
|
||||
<string name="settings.max_bitrate_256">256 Kbps</string>
|
||||
<string name="settings.max_bitrate_32">32 Kbps</string>
|
||||
<string name="settings.max_bitrate_320">320 Kbps</string>
|
||||
<string name="settings.max_bitrate_64">64 Kbps</string>
|
||||
<string name="settings.network_timeout">網路延時</string>
|
||||
<string name="settings.network_timeout_105000">105 秒</string>
|
||||
<string name="settings.network_timeout_45000">45 秒</string>
|
||||
<string name="settings.network_timeout_75000">75 秒</string>
|
||||
<string name="settings.network_timeout_90000">90 秒</string>
|
||||
<string name="settings.notifications_title">通知</string>
|
||||
<string name="settings.network_title">網路</string>
|
||||
<string name="settings.other_title">其他設定</string>
|
||||
<string name="settings.search_15">15</string>
|
||||
<string name="settings.server_color">伺服器顏色</string>
|
||||
<string name="util.bytes_format.byte">0 B</string>
|
||||
<string name="util.bytes_format.gigabyte">0.00 GB</string>
|
||||
<string name="settings.server_username">用戶名</string>
|
||||
<string name="settings.theme_dark">暗色</string>
|
||||
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
|
||||
<string name="util.bytes_format.megabyte">0.00 MB</string>
|
||||
<string name="settings.preload_1000">1000 首歌</string>
|
||||
<string name="settings.server_password">密碼</string>
|
||||
<string name="settings.share_days">天</string>
|
||||
<string name="settings.max_bitrate_128">128 Kbps</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user