Compare commits

..

No commits in common. "4a5f7d67a05caaa34871622b0f83db18fea36625" and "e284cd1fa19a174db5d04dc0cd33882526d91f59" have entirely different histories.

23 changed files with 62 additions and 165 deletions

View File

@ -74,9 +74,7 @@ Unit Tests:
Assemble Release:
stage: Build
script:
- sed -i 's/applicationId \"org.moire.ultrasonic\"/applicationId "org.moire.ultrasonic.gitlab"/' ultrasonic/build.gradle
- ./gradlew assembleRelease
script: ./gradlew assembleRelease
artifacts:
name: ultrasonic-release-unsigned-${CI_COMMIT_SHA}
paths:

View File

@ -1,10 +0,0 @@
#### 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``

View File

@ -52,11 +52,7 @@ style:
active: true
ForbiddenComment:
active: true
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:'
values: ['FIXME:', 'STOPSHIP:']
WildcardImport:
active: true
MaxLineLength:

View File

@ -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=true
android.enableR8.fullMode=false

View File

@ -3,11 +3,11 @@
gradle = "8.1.1"
navigation = "2.5.3"
gradlePlugin = "8.0.2"
gradlePlugin = "8.0.1"
androidxcore = "1.10.1"
ktlint = "0.43.2"
ktlintGradle = "11.3.2"
detekt = "1.23.0"
detekt = "1.22.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 = "5.0.0"
mockitoKotlin = "4.1.0"
kluent = "1.73"
apacheCodecs = "1.15"
robolectric = "4.10.3"

View File

@ -14,7 +14,7 @@ android {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
resourceConfigurations += ['cs', 'de', 'en', 'es', 'fr', 'gl', 'hu', 'it', 'ja', 'nb-rNO', 'nl', 'pl', 'pt', 'pt-rBR', 'ru', 'zh-rCN', 'zh-rTW']
resConfigs '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

View File

@ -1,4 +1,5 @@
#### From Jackson
-keepattributes *Annotation*,EnclosingMethod,Signature
-keepnames class com.fasterxml.jackson.** {
*;

View File

@ -1,14 +1,8 @@
-dontobfuscate
### Don't remove subsonic api serializers/entities
-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 { *; }
-keep class org.moire.ultrasonic.api.subsonic.response.** { *; }
-keep class org.moire.ultrasonic.api.subsonic.models.** { *; }
## Don't remove NowPlayingFragment
-keep class org.moire.ultrasonic.fragment.NowPlayingFragment { *; }

View File

@ -58,7 +58,7 @@ public abstract class BackgroundTask<T> implements ProgressListener
protected String getErrorMessage(Throwable error)
{
return CommunicationError.getErrorMessage(error);
return CommunicationError.getErrorMessage(error, activity);
}
@Override

View File

@ -286,8 +286,7 @@ class NavigationActivity : AppCompatActivity() {
@RequiresApi(Build.VERSION_CODES.N_MR1)
private fun setupAppShortcut() {
if (UApp.instance!!.isFirstRun)
return
// TODO: Handle adding shortcut only if player is ready to add songs (has any servers setup)
val shortcutIntent = Intent(this, NavigationActivity::class.java).apply {
action = Constants.INTENT_PLAY_RANDOM_SONGS
}
@ -399,7 +398,7 @@ class NavigationActivity : AppCompatActivity() {
if (intent == null) return
if (intent.action == Constants.INTENT_PLAY_RANDOM_SONGS) {
val currentFragment = host?.childFragmentManager?.fragments?.last() ?: return
val currentFragment = host!!.childFragmentManager.fragments.last()
val service = MusicServiceFactory.getMusicService()
val musicDirectory = service.getRandomSongs(Settings.maxSongs)
val downloadHandler: DownloadHandler by inject()

View File

@ -142,15 +142,13 @@ 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@launch
// Ignore updates which are not for the current song
if (it.id != song.id) return@subscribe
if (it.rating is HeartRating) {
updateSingleStar(it.rating.isHeart)
} else if (it.rating is StarRating) {
updateFiveStars(it.rating.starRating.toInt())
}
if (it.rating is HeartRating) {
updateSingleStar(it.rating.isHeart)
} else if (it.rating is StarRating) {
updateFiveStars(it.rating.starRating.toInt())
}
}
}

View File

@ -401,7 +401,7 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
Timber.w(exception)
ErrorDialog.Builder(requireContext())
.setTitle(R.string.error_label)
.setMessage(getErrorMessage(exception))
.setMessage(getErrorMessage(exception, context))
.show()
}
}

View File

@ -78,7 +78,6 @@ 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
@ -663,6 +662,7 @@ 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(UApp.applicationContext(), R.string.download_playlist_done)
Util.toast(context, 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)
CommunicationError.getErrorMessage(it, context)
)
Util.toast(UApp.applicationContext(), msg)
Util.toast(context, msg)
}
}
}

View File

@ -351,11 +351,13 @@ open class TrackCollectionFragment(
val isArtist = navArgs.isArtist
// Need a valid id to recurse sub directories stuff
if (hasSubFolders && navArgs.id != null) {
// Need a valid id to download stuff
val id = navArgs.id ?: return
if (hasSubFolders) {
downloadHandler.fetchTracksAndAddToController(
fragment = this,
id = navArgs.id!!,
id = id,
append = append,
autoPlay = !append,
shuffle = shuffle,

View File

@ -505,7 +505,7 @@ class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player {
listeners.queueEvent(Player.EVENT_MEDIA_ITEM_TRANSITION) {
it.onMediaItemTransition(
currentMedia,
Player.MEDIA_ITEM_TRANSITION_REASON_AUTO
Player.MEDIA_ITEM_TRANSITION_REASON_SEEK
)
}
}

View File

@ -14,6 +14,6 @@ data class PlaybackState(
var repeatMode: Int = 0
) : Serializable {
companion object {
private const val serialVersionUID = -293487987L
const val serialVersionUID = -293487987L
}
}

View File

@ -46,7 +46,7 @@ class DownloadHandler(
var successString: String? = null
// Launch the Job
executeTaskWithToast({
executeTaskWithToast(fragment, {
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({
executeTaskWithToast(fragment, {
val songs: MutableList<Track> =
getTracksFromServer(isArtist, id, isDirectory, name, isShare)

View File

@ -20,7 +20,6 @@ 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
@ -47,14 +46,14 @@ object CommunicationError {
ErrorDialog(
context = context,
message = getErrorMessage(error)
message = getErrorMessage(error, context)
).show()
}
@JvmStatic
@Suppress("ReturnCount")
fun getErrorMessage(error: Throwable): String {
val context = UApp.applicationContext()
fun getErrorMessage(error: Throwable, context: Context?): String {
if (context == null) return "Couldn't get Error message, Context is null"
if (error is IOException && !Util.hasUsableNetwork()) {
return context.resources.getString(R.string.background_task_no_network)
} else if (error is FileNotFoundException) {

View File

@ -17,7 +17,6 @@ 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 {
@ -31,6 +30,7 @@ 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)
CommunicationError.getErrorMessage(it, fragment.context)
} else {
successString()
}
@ -49,7 +49,7 @@ fun CoroutineScope.executeTaskWithToast(
if (toastString == null) return@invokeOnCompletion
launch(Dispatchers.Main) {
Util.toast(UApp.applicationContext(), toastString)
Util.toast(fragment.context, toastString)
}
}
@ -62,7 +62,7 @@ fun CoroutineScope.executeTaskWithModalDialog(
successString: () -> String
) {
// Create the job
val job = executeTaskWithToast(task, successString)
val job = executeTaskWithToast(fragment, task, successString)
// Create the dialog
val builder = InfoDialog.Builder(fragment.requireContext())

View File

@ -7,7 +7,7 @@
android:orientation="horizontal"
android:padding="6dp" >
<com.google.android.material.button.MaterialButton
<Button
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" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_play_now"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -31,10 +31,9 @@
android:contentDescription="@string/common.play_now"
app:icon="@drawable/media_start"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none" />
app:iconSize="26dp" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_play_next"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -45,10 +44,9 @@
android:contentDescription="@string/common.play_next"
app:icon="@drawable/ic_play_next"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none" />
app:iconSize="26dp" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_play_last"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -59,11 +57,9 @@
android:contentDescription="@string/common.play_last"
app:icon="@drawable/ic_play_last"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none"
/>
app:iconSize="26dp" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_pin"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -74,11 +70,9 @@
android:contentDescription="@string/common.pin"
app:icon="@drawable/ic_menu_pin"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none"
/>
app:iconSize="26dp" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_unpin"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -89,11 +83,9 @@
android:contentDescription="@string/common.unpin"
app:icon="@drawable/ic_menu_unpin"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none"
/>
app:iconSize="26dp" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_download"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -104,11 +96,9 @@
android:contentDescription="@string/common.download"
app:icon="@drawable/ic_menu_download"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none"
/>
app:iconSize="26dp" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_delete"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -119,11 +109,9 @@
android:contentDescription="@string/common.delete"
app:icon="@drawable/ic_menu_close"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none"
/>
app:iconSize="26dp" />
<com.google.android.material.button.MaterialButton
<Button
android:id="@+id/select_album_more"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="0dp"
@ -134,8 +122,6 @@
android:contentDescription="@string/search.more"
app:icon="@drawable/media_forward"
app:iconGravity="textEnd"
app:iconSize="26dp"
android:scrollbars="none"
/>
app:iconSize="26dp" />
</LinearLayout>

View File

@ -452,5 +452,4 @@
<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>

View File

@ -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">黑色</string>
<string name="settings.theme_black">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,5 +448,4 @@
<string name="language.cs">捷克语</string>
<string name="language.de">德语</string>
<string name="language.pt_BR">葡萄牙语(巴西)</string>
<string name="settings.max_bitrate_pinning">最大比特率 - 永久固定歌曲时</string>
</resources>

View File

@ -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,68 +232,4 @@
<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>