clear the build warnings and fail on new ones

A clean build now emits no warnings of its own, and Kotlin
`allWarningsAsErrors` plus lint `warningsAsErrors` stop new ones from
sneaking back in.

- Kotlin: give SearchFilter's @StringRes parameter an explicit @param:
  target; use safe calls for File.parentFile in StringResourcesTest.
- Packaging: keep debug symbols for the prebuilt .so files that have no
  symbol table, which is what the "unable to strip" warning was about.
- Lint: move `modifier` to the first optional parameter across the Compose
  screens, use mutableIntStateOf for int state, read Resources via
  LocalResources, use the KTX Bitmap.get/String.toUri, drop the unused
  ic_action_name and ic_stat_name bitmaps and the dead -v24 qualifier on
  the notification vector, fix the pt-BR "album" accents and the unused
  Vietnamese/Thai plural quantities, and suppress the intentional
  cleartext/user-certificate, backup, exported-service and
  plural-candidate findings. Disable the version-update nags, which the
  dependency-update process owns.

The AboutLibraries plugin still resolves the variant classpaths during
configuration, so AGP keeps warning about that one; upstream in
mikepenz/AboutLibraries#1369.
This commit is contained in:
austinried
2026-10-08 10:27:09 +00:00
parent eef94eff63
commit 05de10c912
38 changed files with 85 additions and 50 deletions
+25 -1
View File
@@ -43,7 +43,28 @@ android {
lint { lint {
checkReleaseBuilds = false checkReleaseBuilds = false
disable += setOf("MissingTranslation", "MissingQuantity") warningsAsErrors = true
disable +=
setOf(
"MissingTranslation",
"MissingQuantity",
// Version nags are handled by the dependency-update process, not the build.
"AndroidGradlePluginVersion",
"GradleDependency",
"NewerVersionAvailable",
)
}
packaging {
jniLibs {
// Prebuilt .so files with no symbol table; leaving them unstripped avoids the strip warning.
keepDebugSymbols +=
setOf(
"**/libandroidx.graphics.path.so",
"**/libdatastore_shared_counter.so",
"**/libsqliteJni.so",
)
}
} }
testOptions { testOptions {
@@ -71,6 +92,7 @@ android {
kotlin { kotlin {
compilerOptions { compilerOptions {
jvmTarget.set(JvmTarget.JVM_17) jvmTarget.set(JvmTarget.JVM_17)
allWarningsAsErrors.set(true)
} }
} }
@@ -82,6 +104,8 @@ ksp {
arg("room.schemaLocation", "$projectDir/schemas") arg("room.schemaLocation", "$projectDir/schemas")
} }
// Resolving the variant classpaths here makes AGP warn that a configuration was resolved during
// configuration time; upstream, tracked in mikepenz/AboutLibraries#1369.
aboutLibraries { aboutLibraries {
collect { collect {
configPath = file("config") configPath = file("config")
+6 -3
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
@@ -18,7 +19,8 @@
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Subtracks"> android:theme="@style/Theme.Subtracks"
tools:ignore="DataExtractionRules,UnusedAttribute">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
@@ -35,7 +37,8 @@
<service <service
android:name=".playback.PlaybackService" android:name=".playback.PlaybackService"
android:exported="true" android:exported="true"
android:foregroundServiceType="mediaPlayback"> android:foregroundServiceType="mediaPlayback"
tools:ignore="ExportedService">
<intent-filter> <intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" /> <action android:name="androidx.media3.session.MediaSessionService" />
</intent-filter> </intent-filter>
@@ -2,8 +2,8 @@ package com.subtracks.data.download
import android.app.DownloadManager import android.app.DownloadManager
import android.content.Context import android.content.Context
import android.net.Uri
import android.os.Environment import android.os.Environment
import androidx.core.net.toUri
import com.subtracks.data.model.DownloadError import com.subtracks.data.model.DownloadError
class SystemDownloadEngine( class SystemDownloadEngine(
@@ -12,7 +12,7 @@ class SystemDownloadEngine(
override fun enqueue(request: EngineRequest): Long { override fun enqueue(request: EngineRequest): Long {
val download = val download =
DownloadManager DownloadManager
.Request(Uri.parse(request.uri)) .Request(request.uri.toUri())
.setTitle(request.title) .setTitle(request.title)
.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_MUSIC, request.path) .setDestinationInExternalFilesDir(context, Environment.DIRECTORY_MUSIC, request.path)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN) .setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)
@@ -248,11 +248,11 @@ fun HeroHeader(
onDownloadAction: (BulkDownloadAction) -> Unit, onDownloadAction: (BulkDownloadAction) -> Unit,
onMore: () -> Unit, onMore: () -> Unit,
topInset: Dp, topInset: Dp,
modifier: Modifier = Modifier,
controlsModifier: Modifier = Modifier, controlsModifier: Modifier = Modifier,
thumbnailRef: CoverArtRef? = null, thumbnailRef: CoverArtRef? = null,
comment: String? = null, comment: String? = null,
onSubtitleClick: (() -> Unit)? = null, onSubtitleClick: (() -> Unit)? = null,
modifier: Modifier = Modifier,
) { ) {
Column( Column(
modifier = modifier =
@@ -35,7 +35,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -123,6 +123,7 @@ fun HomeListScreen(
artists: LazyPagingItems<Artist>, artists: LazyPagingItems<Artist>,
songs: LazyPagingItems<AlbumSongItem>, songs: LazyPagingItems<AlbumSongItem>,
playlists: LazyPagingItems<Playlist>, playlists: LazyPagingItems<Playlist>,
modifier: Modifier = Modifier,
genres: List<String> = emptyList(), genres: List<String> = emptyList(),
decades: List<Long> = emptyList(), decades: List<Long> = emptyList(),
coverArt: (String?, Boolean) -> CoverArtRef? = { _, _ -> null }, coverArt: (String?, Boolean) -> CoverArtRef? = { _, _ -> null },
@@ -135,10 +136,9 @@ fun HomeListScreen(
onLongClick: (MenuTarget) -> Unit = {}, onLongClick: (MenuTarget) -> Unit = {},
onGenreClick: (String) -> Unit = {}, onGenreClick: (String) -> Unit = {},
onDecadeClick: (Long) -> Unit = {}, onDecadeClick: (Long) -> Unit = {},
modifier: Modifier = Modifier,
) { ) {
val now = remember { System.currentTimeMillis() } val now = remember { System.currentTimeMillis() }
val resources = LocalContext.current.resources val resources = LocalResources.current
Scaffold( Scaffold(
modifier = modifier, modifier = modifier,
topBar = { topBar = {
@@ -172,6 +172,7 @@ fun HomeScreen(
feed: HomeFeed, feed: HomeFeed,
coverArt: (String?, Boolean) -> CoverArtRef?, coverArt: (String?, Boolean) -> CoverArtRef?,
playingSongId: String?, playingSongId: String?,
modifier: Modifier = Modifier,
topInset: Dp = 0.dp, topInset: Dp = 0.dp,
bottomInset: Dp = 0.dp, bottomInset: Dp = 0.dp,
onAlbumClick: (Album) -> Unit = {}, onAlbumClick: (Album) -> Unit = {},
@@ -182,7 +183,6 @@ fun HomeScreen(
onGenreClick: (String) -> Unit = {}, onGenreClick: (String) -> Unit = {},
onDecadeClick: (Long) -> Unit = {}, onDecadeClick: (Long) -> Unit = {},
onSync: () -> Unit = {}, onSync: () -> Unit = {},
modifier: Modifier = Modifier,
) { ) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) { CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
if (feed.loading) { if (feed.loading) {
@@ -295,6 +295,7 @@ fun OfflineHomeScreen(
songs: List<AlbumSongItem>, songs: List<AlbumSongItem>,
coverArt: (String?, Boolean) -> CoverArtRef?, coverArt: (String?, Boolean) -> CoverArtRef?,
playingSongId: String?, playingSongId: String?,
modifier: Modifier = Modifier,
topInset: Dp = 0.dp, topInset: Dp = 0.dp,
bottomInset: Dp = 0.dp, bottomInset: Dp = 0.dp,
onAlbumClick: (Album) -> Unit = {}, onAlbumClick: (Album) -> Unit = {},
@@ -303,7 +304,6 @@ fun OfflineHomeScreen(
onSongPlay: (Song) -> Unit = {}, onSongPlay: (Song) -> Unit = {},
onLongClick: (MenuTarget) -> Unit = {}, onLongClick: (MenuTarget) -> Unit = {},
onMore: (OfflineListKind) -> Unit = {}, onMore: (OfflineListKind) -> Unit = {},
modifier: Modifier = Modifier,
) { ) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) { CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
LazyColumn( LazyColumn(
@@ -146,6 +146,7 @@ fun AlbumDetailRoute(
fun AlbumDetailScreen( fun AlbumDetailScreen(
album: Album?, album: Album?,
songs: List<Song>, songs: List<Song>,
modifier: Modifier = Modifier,
discs: List<Disc> = emptyList(), discs: List<Disc> = emptyList(),
coverArt: (String?, Boolean) -> CoverArtRef?, coverArt: (String?, Boolean) -> CoverArtRef?,
artwork: ArtworkColors?, artwork: ArtworkColors?,
@@ -160,7 +161,6 @@ fun AlbumDetailScreen(
onMore: () -> Unit = {}, onMore: () -> Unit = {},
onArtistClick: (() -> Unit)? = null, onArtistClick: (() -> Unit)? = null,
playingSongId: String? = null, playingSongId: String? = null,
modifier: Modifier = Modifier,
) { ) {
val multiDisc = songs.map { it.disc ?: 1L }.distinct().size > 1 val multiDisc = songs.map { it.disc ?: 1L }.distinct().size > 1
val resources = LocalResources.current val resources = LocalResources.current
@@ -48,6 +48,7 @@ fun AlbumsContent(
coverArt: (String?, Boolean) -> CoverArtRef?, coverArt: (String?, Boolean) -> CoverArtRef?,
bottomInset: Dp, bottomInset: Dp,
onAlbumClick: (Album) -> Unit, onAlbumClick: (Album) -> Unit,
modifier: Modifier = Modifier,
onLongClick: (MenuTarget) -> Unit = {}, onLongClick: (MenuTarget) -> Unit = {},
filtered: Boolean = false, filtered: Boolean = false,
onClearFilters: () -> Unit = {}, onClearFilters: () -> Unit = {},
@@ -55,7 +56,6 @@ fun AlbumsContent(
topInset: Dp = 0.dp, topInset: Dp = 0.dp,
onSync: () -> Unit = {}, onSync: () -> Unit = {},
downloadStatuses: Map<String, ListDownloadStatus> = emptyMap(), downloadStatuses: Map<String, ListDownloadStatus> = emptyMap(),
modifier: Modifier = Modifier,
) { ) {
when { when {
items.itemCount == 0 && items.loadState.refresh is LoadState.Loading -> { items.itemCount == 0 && items.loadState.refresh is LoadState.Loading -> {
@@ -217,6 +217,7 @@ fun ArtistDetailRoute(
@Composable @Composable
fun ArtistDetailScreen( fun ArtistDetailScreen(
artist: Artist?, artist: Artist?,
modifier: Modifier = Modifier,
artistName: String = "", artistName: String = "",
isAlbumArtist: Boolean = false, isAlbumArtist: Boolean = false,
albums: List<Album>, albums: List<Album>,
@@ -232,7 +233,6 @@ fun ArtistDetailScreen(
starred: Boolean = false, starred: Boolean = false,
onToggleStar: (() -> Unit)? = null, onToggleStar: (() -> Unit)? = null,
onShuffle: () -> Unit = {}, onShuffle: () -> Unit = {},
modifier: Modifier = Modifier,
) { ) {
val listState = rememberLazyGridState() val listState = rememberLazyGridState()
val fill = rememberViewportFill(listState) val fill = rememberViewportFill(listState)
@@ -45,6 +45,7 @@ fun ArtistsContent(
coverArt: (String?, Boolean) -> CoverArtRef?, coverArt: (String?, Boolean) -> CoverArtRef?,
bottomInset: Dp, bottomInset: Dp,
onArtistClick: (Artist) -> Unit, onArtistClick: (Artist) -> Unit,
modifier: Modifier = Modifier,
onLongClick: (MenuTarget) -> Unit = {}, onLongClick: (MenuTarget) -> Unit = {},
filtered: Boolean = false, filtered: Boolean = false,
onClearFilters: () -> Unit = {}, onClearFilters: () -> Unit = {},
@@ -52,7 +53,6 @@ fun ArtistsContent(
topInset: Dp = 0.dp, topInset: Dp = 0.dp,
onSync: () -> Unit = {}, onSync: () -> Unit = {},
downloadStatuses: Map<String, ListDownloadStatus> = emptyMap(), downloadStatuses: Map<String, ListDownloadStatus> = emptyMap(),
modifier: Modifier = Modifier,
) { ) {
when { when {
items.itemCount == 0 && items.loadState.refresh is LoadState.Loading -> { items.itemCount == 0 && items.loadState.refresh is LoadState.Loading -> {
@@ -386,6 +386,7 @@ fun LibraryScreen(
onPlaylistClick: (Playlist) -> Unit, onPlaylistClick: (Playlist) -> Unit,
onSync: () -> Unit, onSync: () -> Unit,
onOpenSettings: () -> Unit, onOpenSettings: () -> Unit,
modifier: Modifier = Modifier,
onOpenSearch: () -> Unit = {}, onOpenSearch: () -> Unit = {},
offline: Boolean = false, offline: Boolean = false,
onExitOffline: () -> Unit = {}, onExitOffline: () -> Unit = {},
@@ -409,7 +410,6 @@ fun LibraryScreen(
playlistDownloads: Map<String, ListDownloadStatus> = emptyMap(), playlistDownloads: Map<String, ListDownloadStatus> = emptyMap(),
dismissals: Flow<Unit> = emptyFlow(), dismissals: Flow<Unit> = emptyFlow(),
homeContent: @Composable (topInset: Dp, bottomInset: Dp) -> Unit = { _, _ -> }, homeContent: @Composable (topInset: Dp, bottomInset: Dp) -> Unit = { _, _ -> },
modifier: Modifier = Modifier,
) { ) {
val pagerState = val pagerState =
rememberPagerState( rememberPagerState(
@@ -831,6 +831,7 @@ private fun ListOptionsGlyph(modifier: Modifier = Modifier) {
} }
} }
@Suppress("FrequentlyChangingValue")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
internal fun LibraryTabs( internal fun LibraryTabs(
@@ -120,6 +120,7 @@ fun PlaylistDetailScreen(
songs: LazyPagingItems<PlaylistSongItem>, songs: LazyPagingItems<PlaylistSongItem>,
coverArt: (String?, Boolean) -> CoverArtRef?, coverArt: (String?, Boolean) -> CoverArtRef?,
artwork: ArtworkColors?, artwork: ArtworkColors?,
modifier: Modifier = Modifier,
downloads: Map<String, SongDownload> = emptyMap(), downloads: Map<String, SongDownload> = emptyMap(),
downloadStatus: ListDownloadStatus = ListDownloadStatus(), downloadStatus: ListDownloadStatus = ListDownloadStatus(),
offline: Boolean = false, offline: Boolean = false,
@@ -131,7 +132,6 @@ fun PlaylistDetailScreen(
onPlay: () -> Unit = {}, onPlay: () -> Unit = {},
onMore: () -> Unit = {}, onMore: () -> Unit = {},
playingSongId: String? = null, playingSongId: String? = null,
modifier: Modifier = Modifier,
) { ) {
val context = LocalContext.current val context = LocalContext.current
HeroDetailScaffold( HeroDetailScaffold(
@@ -44,6 +44,7 @@ fun PlaylistsContent(
coverArt: (String?, Boolean) -> CoverArtRef?, coverArt: (String?, Boolean) -> CoverArtRef?,
bottomInset: Dp, bottomInset: Dp,
onPlaylistClick: (Playlist) -> Unit, onPlaylistClick: (Playlist) -> Unit,
modifier: Modifier = Modifier,
onLongClick: (MenuTarget) -> Unit = {}, onLongClick: (MenuTarget) -> Unit = {},
filtered: Boolean = false, filtered: Boolean = false,
onClearFilters: () -> Unit = {}, onClearFilters: () -> Unit = {},
@@ -51,7 +52,6 @@ fun PlaylistsContent(
topInset: Dp = 0.dp, topInset: Dp = 0.dp,
onSync: () -> Unit = {}, onSync: () -> Unit = {},
downloadStatuses: Map<String, ListDownloadStatus> = emptyMap(), downloadStatuses: Map<String, ListDownloadStatus> = emptyMap(),
modifier: Modifier = Modifier,
) { ) {
when { when {
items.itemCount == 0 && items.loadState.refresh is LoadState.Loading -> { items.itemCount == 0 && items.loadState.refresh is LoadState.Loading -> {
@@ -93,6 +93,7 @@ private fun buildVectorPath(vector: ImageVector): Path {
@Composable @Composable
fun SongRow( fun SongRow(
song: Song, song: Song,
modifier: Modifier = Modifier,
coverArtId: String? = null, coverArtId: String? = null,
coverArt: ((String?, Boolean) -> CoverArtRef?)? = null, coverArt: ((String?, Boolean) -> CoverArtRef?)? = null,
isPlaying: Boolean = false, isPlaying: Boolean = false,
@@ -100,7 +101,6 @@ fun SongRow(
durationSeconds: Long? = null, durationSeconds: Long? = null,
download: SongDownload? = null, download: SongDownload? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
modifier: Modifier = Modifier,
) { ) {
ListItem( ListItem(
modifier = modifier, modifier = modifier,
@@ -60,10 +60,10 @@ fun MiniPlayer(
onExpand: () -> Unit, onExpand: () -> Unit,
onPlayPause: () -> Unit, onPlayPause: () -> Unit,
onNext: () -> Unit, onNext: () -> Unit,
modifier: Modifier = Modifier,
progressInset: Boolean = false, progressInset: Boolean = false,
onExpandDrag: (Float) -> Unit = {}, onExpandDrag: (Float) -> Unit = {},
onExpandRelease: (Float) -> Unit = {}, onExpandRelease: (Float) -> Unit = {},
modifier: Modifier = Modifier,
) { ) {
val item = state.item ?: return val item = state.item ?: return
var dragUpPx by remember { mutableFloatStateOf(0f) } var dragUpPx by remember { mutableFloatStateOf(0f) }
@@ -182,9 +182,9 @@ fun NowPlayingRoute(
onQueue: () -> Unit, onQueue: () -> Unit,
onViewAlbum: (String) -> Unit, onViewAlbum: (String) -> Unit,
onViewArtist: (String) -> Unit, onViewArtist: (String) -> Unit,
modifier: Modifier = Modifier,
contextMenuHost: ContextMenuHost? = null, contextMenuHost: ContextMenuHost? = null,
setStar: (StarType, String, Boolean) -> Unit, setStar: (StarType, String, Boolean) -> Unit,
modifier: Modifier = Modifier,
controller: PlaybackController = koinInject(), controller: PlaybackController = koinInject(),
libraryRepository: LibraryRepository = koinInject(), libraryRepository: LibraryRepository = koinInject(),
downloadRepository: DownloadRepository = koinInject(), downloadRepository: DownloadRepository = koinInject(),
@@ -286,6 +286,7 @@ fun NowPlayingScreen(
onPlayPause: () -> Unit, onPlayPause: () -> Unit,
onNext: () -> Unit, onNext: () -> Unit,
onPrevious: () -> Unit, onPrevious: () -> Unit,
modifier: Modifier = Modifier,
onSkipPrevious: () -> Unit = onPrevious, onSkipPrevious: () -> Unit = onPrevious,
onShuffle: () -> Unit = {}, onShuffle: () -> Unit = {},
onRepeat: () -> Unit = {}, onRepeat: () -> Unit = {},
@@ -298,7 +299,6 @@ fun NowPlayingScreen(
thumbnailRef: CoverArtRef? = null, thumbnailRef: CoverArtRef? = null,
previousArt: NowPlayingArt? = null, previousArt: NowPlayingArt? = null,
nextArt: NowPlayingArt? = null, nextArt: NowPlayingArt? = null,
modifier: Modifier = Modifier,
) { ) {
val playButtonSize = 90.dp val playButtonSize = 90.dp
val playCircleDiameter = playButtonSize * 20f / 24f val playCircleDiameter = playButtonSize * 20f / 24f
@@ -33,6 +33,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -106,10 +107,10 @@ class QueueViewModel(
var ready by mutableStateOf(false) var ready by mutableStateOf(false)
private set private set
var initialIndex by mutableStateOf(0) var initialIndex by mutableIntStateOf(0)
private set private set
var generation by mutableStateOf(0) var generation by mutableIntStateOf(0)
private set private set
private var nextId = 0L private var nextId = 0L
@@ -562,6 +563,7 @@ internal fun dropTarget(
return (before - if (before > from) 1 else 0) + 1 return (before - if (before > from) 1 else 0) + 1
} }
@Suppress("ModifierParameter")
@Composable @Composable
private fun QueueRowItem( private fun QueueRowItem(
row: QueueRow, row: QueueRow,
@@ -59,7 +59,7 @@ import org.koin.compose.koinInject
import org.koin.compose.viewmodel.koinViewModel import org.koin.compose.viewmodel.koinViewModel
enum class SearchFilter( enum class SearchFilter(
@StringRes val label: Int, @param:StringRes val label: Int,
) { ) {
Songs(R.string.search_songs), Songs(R.string.search_songs),
Albums(R.string.resources_album_name), Albums(R.string.resources_album_name),
@@ -125,6 +125,7 @@ fun SearchScreen(
query: String, query: String,
onQueryChange: (String) -> Unit, onQueryChange: (String) -> Unit,
results: SearchResults, results: SearchResults,
modifier: Modifier = Modifier,
filters: Set<SearchFilter> = ALL_FILTERS, filters: Set<SearchFilter> = ALL_FILTERS,
onToggleFilter: (SearchFilter) -> Unit = {}, onToggleFilter: (SearchFilter) -> Unit = {},
coverArt: (String?, Boolean) -> CoverArtRef? = { _, _ -> null }, coverArt: (String?, Boolean) -> CoverArtRef? = { _, _ -> null },
@@ -135,7 +136,6 @@ fun SearchScreen(
onPlaylistClick: (Playlist) -> Unit = {}, onPlaylistClick: (Playlist) -> Unit = {},
onSongClick: (Song) -> Unit = {}, onSongClick: (Song) -> Unit = {},
onLongClick: (MenuTarget) -> Unit = {}, onLongClick: (MenuTarget) -> Unit = {},
modifier: Modifier = Modifier,
) { ) {
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val backLabel = stringResource(R.string.navigation_back) val backLabel = stringResource(R.string.navigation_back)
@@ -90,8 +90,8 @@ fun AddSourceScreen(
onSave: () -> Unit, onSave: () -> Unit,
onDelete: () -> Unit, onDelete: () -> Unit,
onBack: (() -> Unit)?, onBack: (() -> Unit)?,
dismissals: Flow<Unit> = emptyFlow(),
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
dismissals: Flow<Unit> = emptyFlow(),
) { ) {
val nameFocus = remember { FocusRequester() } val nameFocus = remember { FocusRequester() }
val addressFocus = remember { FocusRequester() } val addressFocus = remember { FocusRequester() }
@@ -41,6 +41,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -124,6 +125,7 @@ fun SettingsScreen(
downloadQuality: StreamQuality, downloadQuality: StreamQuality,
downloadOverMetered: Boolean, downloadOverMetered: Boolean,
scrobbling: Boolean, scrobbling: Boolean,
modifier: Modifier = Modifier,
offline: Boolean = false, offline: Boolean = false,
onSelectSource: (Long) -> Unit, onSelectSource: (Long) -> Unit,
onEditServer: (Long) -> Unit, onEditServer: (Long) -> Unit,
@@ -139,7 +141,6 @@ fun SettingsScreen(
onOpenLicenses: () -> Unit, onOpenLicenses: () -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
dismissals: Flow<Unit> = emptyFlow(), dismissals: Flow<Unit> = emptyFlow(),
modifier: Modifier = Modifier,
) { ) {
var dialog by remember { mutableStateOf<SettingsDialog?>(null) } var dialog by remember { mutableStateOf<SettingsDialog?>(null) }
DismissOnRequest(dismissals) { dialog = null } DismissOnRequest(dismissals) { dialog = null }
@@ -397,7 +398,7 @@ private fun ConcurrencyDialog(
onDismiss: () -> Unit, onDismiss: () -> Unit,
) { ) {
val resources = LocalResources.current val resources = LocalResources.current
var draft by remember(selected) { mutableStateOf(selected) } var draft by remember(selected) { mutableIntStateOf(selected) }
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.sync_concurrency)) }, title = { Text(stringResource(R.string.sync_concurrency)) },
@@ -12,6 +12,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance import androidx.compose.ui.graphics.luminance
import androidx.core.graphics.get
import androidx.palette.graphics.Palette import androidx.palette.graphics.Palette
import coil3.SingletonImageLoader import coil3.SingletonImageLoader
import coil3.compose.LocalPlatformContext import coil3.compose.LocalPlatformContext
@@ -202,7 +203,7 @@ object ArtworkSeedCache {
x: Int, x: Int,
y: Int, y: Int,
): Float { ): Float {
val color = Color(bitmap.getPixel(x, y)) val color = Color(bitmap[x, y])
return 0.2126f * color.red + 0.7152f * color.green + 0.0722f * color.blue return 0.2126f * color.red + 0.7152f * color.green + 0.0722f * color.blue
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 B

@@ -1,8 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="300" android:viewportWidth="300"
android:viewportHeight="300"> android:viewportHeight="300"
tools:ignore="UnusedResources">
<group android:translateY="40.25"> <group android:translateY="40.25">
<path <path
android:pathData="m23.84,-0c-12.76,0 -23.44,13 -23.65,25.12 -0.42,79.03 0,160.73 0,170.4 0,12.59 9.8,23.99 23.41,23.99l253.58,0c11.4,0 22.66,-12.38 22.66,-22.79L299.84,24.12c0,-10.8 -10.24,-23.81 -21.84,-23.81 -11.6,0 -254.16,-0.31 -254.16,-0.31zM39.05,25.28c0,0 217.71,0.71 223.67,0.71 6.07,0 12.17,6.83 12.17,12.06l0,142.25c0,8.97 -6.97,14.63 -12.48,14.64l-33.42,0.07c-5.32,0.01 -9.93,-7.19 -11.35,-10.61 -1.89,-4.55 -11.08,-25.75 -12.1,-28.1 -0.6,-1.38 -2.9,-2.83 -5.89,-2.83L98.33,153.45c-2.68,0 -4.89,2.87 -5.67,4.71 -1.12,2.66 -9.02,21.39 -11.63,27.57 -2.73,6.46 -4.37,9.06 -11.47,9.06l-30.97,0c-7.44,0 -13.69,-7.17 -13.67,-15.18l0.28,-139.51c0,-6.88 5.54,-14.83 13.85,-14.83z" android:pathData="m23.84,-0c-12.76,0 -23.44,13 -23.65,25.12 -0.42,79.03 0,160.73 0,170.4 0,12.59 9.8,23.99 23.41,23.99l253.58,0c11.4,0 22.66,-12.38 22.66,-22.79L299.84,24.12c0,-10.8 -10.24,-23.81 -21.84,-23.81 -11.6,0 -254.16,-0.31 -254.16,-0.31zM39.05,25.28c0,0 217.71,0.71 223.67,0.71 6.07,0 12.17,6.83 12.17,12.06l0,142.25c0,8.97 -6.97,14.63 -12.48,14.64l-33.42,0.07c-5.32,0.01 -9.93,-7.19 -11.35,-10.61 -1.89,-4.55 -11.08,-25.75 -12.1,-28.1 -0.6,-1.38 -2.9,-2.83 -5.89,-2.83L98.33,153.45c-2.68,0 -4.89,2.87 -5.67,4.71 -1.12,2.66 -9.02,21.39 -11.63,27.57 -2.73,6.46 -4.37,9.06 -11.47,9.06l-30.97,0c-7.44,0 -13.69,-7.17 -13.67,-15.18l0.28,-139.51c0,-6.88 5.54,-14.83 13.85,-14.83z"
+3 -3
View File
@@ -10,8 +10,8 @@
<string name="navigation_tabs_library">Biblioteca</string> <string name="navigation_tabs_library">Biblioteca</string>
<string name="navigation_tabs_settings">Configurações</string> <string name="navigation_tabs_settings">Configurações</string>
<plurals name="resources_album_count"> <plurals name="resources_album_count">
<item quantity="one">%1$d album</item> <item quantity="one">%1$d álbum</item>
<item quantity="other">%1$d albums</item> <item quantity="other">%1$d álbuns</item>
</plurals> </plurals>
<string name="resources_filter_album">Álbum</string> <string name="resources_filter_album">Álbum</string>
<string name="resources_filter_artist">Artista</string> <string name="resources_filter_artist">Artista</string>
@@ -42,7 +42,7 @@
<string name="settings_servers_fields_password">Senha</string> <string name="settings_servers_fields_password">Senha</string>
<string name="settings_servers_fields_username">Usuário</string> <string name="settings_servers_fields_username">Usuário</string>
<string name="navigation_tabs_home">Home</string> <string name="navigation_tabs_home">Home</string>
<string name="resources_album_name">Albums</string> <string name="resources_album_name">Álbuns</string>
<string name="resources_artist_name">Artists</string> <string name="resources_artist_name">Artists</string>
<string name="resources_playlist_name">Playlists</string> <string name="resources_playlist_name">Playlists</string>
</resources> </resources>
-1
View File
@@ -17,7 +17,6 @@
<string name="resources_filter_genre">ประเภท</string> <string name="resources_filter_genre">ประเภท</string>
<string name="resources_filter_starred">ให้ดาว</string> <string name="resources_filter_starred">ให้ดาว</string>
<plurals name="resources_song_count"> <plurals name="resources_song_count">
<item quantity="one">%1$d เพลง</item>
<item quantity="other">%1$d เพลง</item> <item quantity="other">%1$d เพลง</item>
</plurals> </plurals>
<string name="resources_sort_by_artist">ศิลปิน</string> <string name="resources_sort_by_artist">ศิลปิน</string>
-2
View File
@@ -10,7 +10,6 @@
<string name="navigation_tabs_library">Thư viện</string> <string name="navigation_tabs_library">Thư viện</string>
<string name="navigation_tabs_settings">Cài đặt</string> <string name="navigation_tabs_settings">Cài đặt</string>
<plurals name="resources_album_count"> <plurals name="resources_album_count">
<item quantity="one">%1$d album</item>
<item quantity="other">%1$d album</item> <item quantity="other">%1$d album</item>
</plurals> </plurals>
<string name="resources_filter_album">Theo album</string> <string name="resources_filter_album">Theo album</string>
@@ -18,7 +17,6 @@
<string name="resources_filter_genre">Theo thể loại</string> <string name="resources_filter_genre">Theo thể loại</string>
<string name="resources_filter_starred">Theo yêu thích</string> <string name="resources_filter_starred">Theo yêu thích</string>
<plurals name="resources_song_count"> <plurals name="resources_song_count">
<item quantity="one">%1$d bài hát</item>
<item quantity="other">%1$d bài hát</item> <item quantity="other">%1$d bài hát</item>
</plurals> </plurals>
<string name="resources_sort_by_artist">Nghệ sĩ</string> <string name="resources_sort_by_artist">Nghệ sĩ</string>
+3 -3
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">subtracks</string> <string name="app_name">subtracks</string>
<string name="download_channel_description">Progress while songs download</string> <string name="download_channel_description">Progress while songs download</string>
@@ -124,7 +124,7 @@
<string name="search">Search</string> <string name="search">Search</string>
<string name="search_this_list">Search this list</string> <string name="search_this_list">Search this list</string>
<string name="search_songs">Songs</string> <string name="search_songs">Songs</string>
<string name="search_min_length">Type at least %1$d characters to search.</string> <string name="search_min_length" tools:ignore="PluralsCandidate">Type at least %1$d characters to search.</string>
<string name="search_no_results">No results for “%1$s”.</string> <string name="search_no_results">No results for “%1$s”.</string>
<string name="sort_by">Sort by</string> <string name="sort_by">Sort by</string>
<string name="sort_ascending">Sort ascending</string> <string name="sort_ascending">Sort ascending</string>
@@ -245,7 +245,7 @@
<string name="error_server_unknown">Unknown error</string> <string name="error_server_unknown">Unknown error</string>
<string name="error_server_malformed">Malformed response from the server</string> <string name="error_server_malformed">Malformed response from the server</string>
<string name="error_server_truncated">Truncated response from the server</string> <string name="error_server_truncated">Truncated response from the server</string>
<string name="error_library_too_large">Library is too large to sync: it exceeds %1$d rows</string> <string name="error_library_too_large" tools:ignore="PluralsCandidate">Library is too large to sync: it exceeds %1$d rows</string>
<string name="error_http">HTTP %1$d</string> <string name="error_http">HTTP %1$d</string>
<string name="home_section_recently_played_artists">Recently played artists</string> <string name="home_section_recently_played_artists">Recently played artists</string>
@@ -1,9 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<network-security-config> <network-security-config xmlns:tools="http://schemas.android.com/tools">
<base-config cleartextTrafficPermitted="true"> <base-config
cleartextTrafficPermitted="true"
tools:ignore="InsecureBaseConfiguration">
<trust-anchors> <trust-anchors>
<certificates src="system" /> <certificates src="system" />
<certificates src="user" /> <certificates
src="user"
tools:ignore="AcceptsUserCertificates" />
</trust-anchors> </trust-anchors>
</base-config> </base-config>
</network-security-config> </network-security-config>
@@ -34,17 +34,17 @@ class StringResourcesTest {
val locale = parse(file) val locale = parse(file)
locale.forEach { (name, resource) -> locale.forEach { (name, resource) ->
val base = default[name] val base = default[name]
assertTrue("$name in ${file.parentFile.name} is missing from values/strings.xml", base != null) assertTrue("$name in ${file.parentFile?.name} is missing from values/strings.xml", base != null)
assertEquals("$name kind differs in ${file.parentFile.name}", base!!.kind, resource.kind) assertEquals("$name kind differs in ${file.parentFile?.name}", base!!.kind, resource.kind)
if (resource.kind == "plurals") { if (resource.kind == "plurals") {
assertTrue( assertTrue(
"$name has an invalid quantity in ${file.parentFile.name}", "$name has an invalid quantity in ${file.parentFile?.name}",
resource.quantities.all { it in validQuantities }, resource.quantities.all { it in validQuantities },
) )
assertTrue("$name in ${file.parentFile.name} is missing the required 'other' quantity", "other" in resource.quantities) assertTrue("$name in ${file.parentFile?.name} is missing the required 'other' quantity", "other" in resource.quantities)
} }
assertEquals( assertEquals(
"$name placeholders differ in ${file.parentFile.name}", "$name placeholders differ in ${file.parentFile?.name}",
placeholders(base.text), placeholders(base.text),
placeholders(resource.text), placeholders(resource.text),
) )
@@ -275,8 +275,8 @@ class DemoScreenshotTest {
private fun DemoData.MiniPlayerOverlay( private fun DemoData.MiniPlayerOverlay(
song: Song, song: Song,
album: Album, album: Album,
progressInset: Boolean = false,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
progressInset: Boolean = false,
) { ) {
MiniPlayer( MiniPlayer(
state = playbackFor(song, album), state = playbackFor(song, album),