From 000ab89373568df9cc03ce036a2914c30a9f7010 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 22:31:51 +0000 Subject: [PATCH 01/15] chore(deps): bump the gradle-dependencies group across 1 directory with 5 updates Bumps the gradle-dependencies group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [com.google.genai:google-genai](https://github.com/googleapis/java-genai) | `1.53.0` | `1.55.0` | | [io.netty:netty-common](https://github.com/netty/netty) | `4.2.13.Final` | `4.2.14.Final` | | [io.netty:netty-handler](https://github.com/netty/netty) | `4.2.13.Final` | `4.2.14.Final` | | [io.netty:netty-codec-http](https://github.com/netty/netty) | `4.2.13.Final` | `4.2.14.Final` | | [io.netty:netty-codec-http2](https://github.com/netty/netty) | `4.2.13.Final` | `4.2.14.Final` | Updates `com.google.genai:google-genai` from 1.53.0 to 1.55.0 - [Release notes](https://github.com/googleapis/java-genai/releases) - [Changelog](https://github.com/googleapis/java-genai/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-genai/compare/v1.53.0...v1.55.0) Updates `io.netty:netty-common` from 4.2.13.Final to 4.2.14.Final - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.2.13.Final...netty-4.2.14.Final) Updates `io.netty:netty-handler` from 4.2.13.Final to 4.2.14.Final - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.2.13.Final...netty-4.2.14.Final) Updates `io.netty:netty-codec-http` from 4.2.13.Final to 4.2.14.Final - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.2.13.Final...netty-4.2.14.Final) Updates `io.netty:netty-codec-http2` from 4.2.13.Final to 4.2.14.Final - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.2.13.Final...netty-4.2.14.Final) Updates `io.netty:netty-handler` from 4.2.13.Final to 4.2.14.Final - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.2.13.Final...netty-4.2.14.Final) Updates `io.netty:netty-codec-http` from 4.2.13.Final to 4.2.14.Final - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.2.13.Final...netty-4.2.14.Final) Updates `io.netty:netty-codec-http2` from 4.2.13.Final to 4.2.14.Final - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.2.13.Final...netty-4.2.14.Final) --- updated-dependencies: - dependency-name: com.google.genai:google-genai dependency-version: 1.54.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies - dependency-name: io.netty:netty-codec-http dependency-version: 4.2.14.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: io.netty:netty-codec-http dependency-version: 4.2.14.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: io.netty:netty-codec-http2 dependency-version: 4.2.14.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: io.netty:netty-codec-http2 dependency-version: 4.2.14.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: io.netty:netty-common dependency-version: 4.2.14.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: io.netty:netty-handler dependency-version: 4.2.14.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies - dependency-name: io.netty:netty-handler dependency-version: 4.2.14.Final dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1d8e45488..911431378 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ accompanistDrawablepainter = "0.37.3" agp = "9.2.1" app = "1.7.0" -googleGenai = "1.53.0" +googleGenai = "1.55.0" googlePlayServicesCast = "22.3.1" animation = "1.11.2" appcompat = "1.7.1" @@ -65,7 +65,7 @@ junit5 = "6.1.0" kuromoji = "0.9.0" pinyin4j = "2.5.1" securityCrypto = "1.1.0" -netty = "4.2.13.Final" +netty = "4.2.14.Final" bouncycastle = "1.84" commons-lang3 = "3.20.0" jdom2 = "2.0.6.1" From 4e7cd15ed397389158c955b832cfc6b27e2dbe81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 02:08:01 +0000 Subject: [PATCH 02/15] chore(deps): bump com.google.devtools.ksp Bumps the gradle-dependencies group with 1 update: [com.google.devtools.ksp](https://github.com/google/ksp). Updates `com.google.devtools.ksp` from 2.3.8 to 2.3.9 - [Release notes](https://github.com/google/ksp/releases) - [Commits](https://github.com/google/ksp/compare/2.3.8...2.3.9) --- updated-dependencies: - dependency-name: com.google.devtools.ksp dependency-version: 2.3.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 911431378..989d74387 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -77,7 +77,7 @@ dagger = "2.59.2" javax-inject = "1" # Annotations procesing -ksp = "2.3.8" +ksp = "2.3.9" smoothCornerRectAndroidCompose = "v1.0.0" spleeterAndroidIos = "1.0.2" tensorflowLite = "2.17.0" From 2443eeca863ee1aee1b85be266aa36f82fdfdf75 Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Thu, 28 May 2026 16:55:09 -0500 Subject: [PATCH 03/15] chore: modernize dependencies, improve build configuration, and code cleanup Refactor the build system to use version catalog bundles and enhance project configuration for better performance and security. This change also addresses various lint warnings and deprecations across the codebase. - **Build & Dependencies**: - Migrated individual dependencies to `libs.bundles` for Compose, Hilt, Room, Paging, Media3, and Networking in both `app` and `wear` modules. - Enabled Gradle build caching and incremental KSP in `gradle.properties`. - Enabled `nonTransitiveRClass` and disabled `Jetifier` to improve build speeds. - Configured lint to check release builds while allowing the build to continue on errors. - **Security & Networking**: - Updated `network_security_config.xml` to disable cleartext traffic by default, restricting it to loopback addresses. - Refactored `CastTransferStateHolder` to improve `HttpURLConnection` handling. - **Code Cleanup & Modernization**: - Suppressed `DEPRECATION` warnings for `ModalBottomSheet` and `MediaRouter` APIs across the UI layer. - Replaced `println` with `Timber` logging in `PlayerViewModel`. - Removed redundant type casts and simplified nullable string handling in `ConnectivityStateHolder` and `AccountsViewModel`. - Updated `EditSongSheet` to use `mutableFloatStateOf` and corrected `rememberTransformableState` lambda parameters. - Fixed string resource formatting for `batch_edit_partial_success` in `strings.xml`. - **Repository Improvements**: - Cleaned up unused imports and organized dependencies in `MusicRepositoryImpl`. - Simplified logic in `MediaStoreSongRepository` by removing unnecessary type casts. --- app/build.gradle.kts | 43 +++--------- app/src/main/baseline-prof.txt | 2 +- .../data/database/PixelPlayDatabase.kt | 14 ++-- .../data/remote/qqmusic/QQSignGenerator.kt | 13 ++-- .../repository/MediaStoreSongRepository.kt | 2 +- .../data/repository/MusicRepositoryImpl.kt | 49 ++++++-------- .../components/AiMetadataSheet.kt | 1 + .../components/AiPlaylistSheet.kt | 1 + .../AlbumMultiSelectionOptionSheet.kt | 1 + .../components/CastBottomSheet.kt | 1 + .../components/CustomPresetsSheet.kt | 1 + .../presentation/components/DailyMixMenu.kt | 1 + .../presentation/components/EditSongSheet.kt | 45 +++++-------- .../components/GenreSortBottomSheet.kt | 1 + .../components/LibrarySortBottomSheet.kt | 1 + .../presentation/components/LyricsSheet.kt | 1 + .../components/MultiSelectionBottomSheet.kt | 1 + .../components/PlaylistBottomSheet.kt | 1 + .../PlaylistMultiSelectionBottomSheet.kt | 1 + .../components/ReorderTabsSheet.kt | 2 + .../components/RoundedParallaxCarousell.kt | 2 +- .../components/SongInfoBottomSheet.kt | 1 + .../components/SongPickerBottomSheet.kt | 1 + .../components/TimerOptionsBottomSheet.kt | 1 + .../components/player/FullPlayerContent.kt | 1 + .../presentation/screens/HomeScreen.kt | 2 + .../presentation/screens/LibraryMediaTabs.kt | 4 +- .../presentation/screens/LibraryScreen.kt | 1 + .../presentation/screens/LibrarySongsTab.kt | 2 +- .../presentation/screens/MashupScreen.kt | 1 + .../screens/PlaylistDetailScreen.kt | 1 + .../screens/SettingsCategoryScreen.kt | 1 + .../channel/TelegramChannelSearchSheet.kt | 1 + .../dashboard/TelegramDashboardScreen.kt | 1 + .../viewmodel/AccountsViewModel.kt | 12 ++-- .../presentation/viewmodel/CastStateHolder.kt | 1 + .../viewmodel/CastTransferStateHolder.kt | 5 +- .../viewmodel/ConnectivityStateHolder.kt | 7 +- .../viewmodel/GenreDetailViewModel.kt | 16 ++--- .../viewmodel/PlaybackStateHolder.kt | 2 +- .../presentation/viewmodel/PlayerViewModel.kt | 4 +- app/src/main/res/values/strings.xml | 2 +- .../main/res/xml/network_security_config.xml | 7 +- gradle.properties | 4 ++ gradle/libs.versions.toml | 59 +++++++++++++++++ wear/build.gradle.kts | 66 +++++++------------ 46 files changed, 208 insertions(+), 179 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 34dd762b1..076a27dac 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -130,7 +130,8 @@ android { } lint { - checkReleaseBuilds = false + checkReleaseBuilds = true + abortOnError = false // Keep it from failing the build entirely on CI if you have minor warnings } splits { @@ -196,52 +197,29 @@ dependencies { // AndroidX & Compose implementation(libs.androidx.core.ktx) - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.lifecycle.runtime.compose) - implementation(libs.lifecycleprocess) + implementation(libs.bundles.lifecycle) implementation(libs.androidx.activity.compose) implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.ui) - implementation(libs.androidx.ui.graphics) - implementation(libs.androidx.ui.tooling.preview) - implementation(libs.androidx.compose.material3) - implementation(libs.androidx.material.icons.core) - implementation(libs.androidx.material.icons.extended) - implementation(libs.androidx.constraintlayout.compose) - implementation(libs.androidx.foundation) - implementation(libs.androidx.animation) + implementation(libs.bundles.compose) implementation(libs.androidx.palette.ktx) implementation(libs.androidx.core.splashscreen) - implementation(libs.androidx.ui.text.google.fonts) implementation(libs.material) implementation(libs.androidx.appcompat) // DI & Navigation - implementation(libs.hilt.android) + implementation(libs.bundles.hilt) ksp(libs.hilt.android.compiler) - implementation(libs.androidx.hilt.navigation.compose) - implementation(libs.androidx.hilt.lifecycle.viewmodel.compose) - implementation(libs.androidx.hilt.work) ksp(libs.androidx.hilt.compiler) implementation(libs.androidx.navigation.compose) - implementation(libs.androidx.navigation.runtime.ktx) // Storage & Paging - implementation(libs.androidx.room.runtime) + implementation(libs.bundles.room) ksp(libs.androidx.room.compiler) - implementation(libs.androidx.room.ktx) - implementation(libs.androidx.room.paging) - implementation(libs.androidx.paging.runtime) - implementation(libs.androidx.paging.compose) - implementation(libs.androidx.paging.common) + implementation(libs.bundles.paging) // Media & Files - implementation(libs.androidx.media3.exoplayer) - implementation(libs.androidx.media3.ui) - implementation(libs.androidx.media3.session) + implementation(libs.bundles.media3) implementation(libs.androidx.media3.exoplayer.ffmpeg) - implementation(libs.androidx.media3.exoplayer.midi) - implementation(libs.androidx.media3.transformer) implementation(libs.androidx.mediarouter) implementation(libs.androidx.media) implementation(libs.coil.compose) @@ -252,10 +230,7 @@ dependencies { implementation(libs.androidx.graphics.shapes) // Networking & Serialization - implementation(libs.retrofit) - implementation(libs.converter.gson) - implementation(libs.okhttp) - implementation(libs.logging.interceptor) + implementation(libs.bundles.networking) implementation(libs.gson) implementation(libs.kotlinx.serialization.json) implementation(libs.kotlinx.collections.immutable) diff --git a/app/src/main/baseline-prof.txt b/app/src/main/baseline-prof.txt index ff6b9d94e..4d909584f 100644 --- a/app/src/main/baseline-prof.txt +++ b/app/src/main/baseline-prof.txt @@ -5997,7 +5997,7 @@ Landroidx/compose/material3/MinimumInteractiveModifierNode$$ExternalSyntheticLam SPLandroidx/compose/material3/MinimumInteractiveModifierNode$$ExternalSyntheticLambda0;->(ILandroidx/compose/ui/layout/Placeable;I)V PLandroidx/compose/material3/MinimumInteractiveModifierNode$$ExternalSyntheticLambda0;->invoke(Ljava/lang/Object;)Ljava/lang/Object; Landroidx/compose/material3/ModalBottomSheetKt; -HSPLandroidx/compose/material3/ModalBottomSheetKt;->rememberModalBottomSheetState(ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Landroidx/compose/material3/SheetState; +HSPLandroidx/compose/material3/ModalBottomSheetKt;->rememberBottomSheetState(ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/Composer;Landroidx/compose/material3/SheetState; Landroidx/compose/material3/ModalBottomSheetKt$$ExternalSyntheticLambda3; SPLandroidx/compose/material3/ModalBottomSheetKt$$ExternalSyntheticLambda3;->()V Landroidx/compose/material3/MotionScheme; diff --git a/app/src/main/java/com/theveloper/pixelplay/data/database/PixelPlayDatabase.kt b/app/src/main/java/com/theveloper/pixelplay/data/database/PixelPlayDatabase.kt index 2f7cca438..c6cd4392b 100644 --- a/app/src/main/java/com/theveloper/pixelplay/data/database/PixelPlayDatabase.kt +++ b/app/src/main/java/com/theveloper/pixelplay/data/database/PixelPlayDatabase.kt @@ -291,19 +291,19 @@ abstract class PixelPlayDatabase : RoomDatabase() { } val MIGRATION_18_19 = object : Migration(18, 19) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL( + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( "CREATE TABLE IF NOT EXISTS `lyrics` (`songId` INTEGER NOT NULL, `content` TEXT NOT NULL, `isSynced` INTEGER NOT NULL DEFAULT 0, `source` TEXT, PRIMARY KEY(`songId`))" ) - database.execSQL( + db.execSQL( "INSERT INTO lyrics (songId, content) SELECT id, lyrics FROM songs WHERE lyrics IS NOT NULL AND lyrics != ''" ) } } val MIGRATION_14_15 = object : Migration(14, 15) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL( + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( "ALTER TABLE album_art_themes ADD COLUMN paletteStyle TEXT NOT NULL DEFAULT 'tonal_spot'" ) @@ -332,14 +332,14 @@ abstract class PixelPlayDatabase : RoomDatabase() { val prefixes = listOf("light_", "dark_") prefixes.forEach { prefix -> newRoleColumns.forEach { role -> - database.execSQL( + db.execSQL( "ALTER TABLE album_art_themes ADD COLUMN ${prefix}${role} TEXT NOT NULL DEFAULT '#00000000'" ) } } // The table is a cache; wipe stale rows so we always regenerate with full token data. - database.execSQL("DELETE FROM album_art_themes") + db.execSQL("DELETE FROM album_art_themes") } } diff --git a/app/src/main/java/com/theveloper/pixelplay/data/remote/qqmusic/QQSignGenerator.kt b/app/src/main/java/com/theveloper/pixelplay/data/remote/qqmusic/QQSignGenerator.kt index 3a1995d00..a8abfb35a 100644 --- a/app/src/main/java/com/theveloper/pixelplay/data/remote/qqmusic/QQSignGenerator.kt +++ b/app/src/main/java/com/theveloper/pixelplay/data/remote/qqmusic/QQSignGenerator.kt @@ -1,7 +1,6 @@ package com.theveloper.pixelplay.data.remote.qqmusic import android.content.Context -import android.os.Build import android.os.Handler import android.os.Looper import android.util.Base64 @@ -76,14 +75,12 @@ class QQSignGenerator(private val context: Context) { domStorageEnabled = true allowFileAccess = false allowContentAccess = false - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - allowFileAccessFromFileURLs = false - allowUniversalAccessFromFileURLs = false - } + @Suppress("DEPRECATION") + allowFileAccessFromFileURLs = false + @Suppress("DEPRECATION") + allowUniversalAccessFromFileURLs = false mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - safeBrowsingEnabled = true - } + safeBrowsingEnabled = true } webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView?, url: String?) { diff --git a/app/src/main/java/com/theveloper/pixelplay/data/repository/MediaStoreSongRepository.kt b/app/src/main/java/com/theveloper/pixelplay/data/repository/MediaStoreSongRepository.kt index 735a18c45..0367a785b 100644 --- a/app/src/main/java/com/theveloper/pixelplay/data/repository/MediaStoreSongRepository.kt +++ b/app/src/main/java/com/theveloper/pixelplay/data/repository/MediaStoreSongRepository.kt @@ -382,7 +382,7 @@ class MediaStoreSongRepository @Inject constructor( val minDuration = values[3] as Int Triple(allowedDirs, blockedDirs, minDuration) }.flatMapLatest { (allowedDirs, blockedDirs, minDuration) -> - val minDurationMs = minDuration as Int + val minDurationMs = minDuration val musicIds = getFilteredSongIds(allowedDirs.toList(), blockedDirs.toList(), minDurationMs) val genreMap = getSongIdToGenreMap(context.contentResolver) diff --git a/app/src/main/java/com/theveloper/pixelplay/data/repository/MusicRepositoryImpl.kt b/app/src/main/java/com/theveloper/pixelplay/data/repository/MusicRepositoryImpl.kt index 8ebd428cc..c90ca7220 100644 --- a/app/src/main/java/com/theveloper/pixelplay/data/repository/MusicRepositoryImpl.kt +++ b/app/src/main/java/com/theveloper/pixelplay/data/repository/MusicRepositoryImpl.kt @@ -1,42 +1,32 @@ package com.theveloper.pixelplay.data.repository -// import kotlinx.coroutines.withContext // May not be needed for Flow transformations - -// import kotlinx.coroutines.sync.withLock // May not be needed if directoryScanMutex logic changes - import android.content.Context import android.net.Uri import android.os.Environment import android.provider.MediaStore import android.util.Log - -import com.theveloper.pixelplay.data.model.Song -import com.theveloper.pixelplay.data.repository.ArtistImageRepository -import dagger.Lazy -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map -import javax.inject.Inject -import javax.inject.Singleton import androidx.core.net.toUri +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.filter +import androidx.paging.map import com.theveloper.pixelplay.data.database.FavoritesDao import com.theveloper.pixelplay.data.database.MusicDao import com.theveloper.pixelplay.data.database.SearchHistoryDao import com.theveloper.pixelplay.data.database.SearchHistoryEntity import com.theveloper.pixelplay.data.database.TelegramChannelEntity import com.theveloper.pixelplay.data.database.TelegramDao +import com.theveloper.pixelplay.data.database.TelegramTopicEntity import com.theveloper.pixelplay.data.database.toAlbum import com.theveloper.pixelplay.data.database.toArtist import com.theveloper.pixelplay.data.database.toSearchHistoryItem import com.theveloper.pixelplay.data.database.toSong import com.theveloper.pixelplay.data.database.toTelegramEntity import com.theveloper.pixelplay.data.database.toTelegramEntityWithThread -import com.theveloper.pixelplay.data.database.TelegramTopicEntity import com.theveloper.pixelplay.data.model.Album import com.theveloper.pixelplay.data.model.Artist +import com.theveloper.pixelplay.data.model.FolderSource import com.theveloper.pixelplay.data.model.Genre import com.theveloper.pixelplay.data.model.Lyrics import com.theveloper.pixelplay.data.model.LyricsSourcePreference @@ -45,8 +35,8 @@ import com.theveloper.pixelplay.data.model.Playlist import com.theveloper.pixelplay.data.model.SearchFilterType import com.theveloper.pixelplay.data.model.SearchHistoryItem import com.theveloper.pixelplay.data.model.SearchResultItem +import com.theveloper.pixelplay.data.model.Song import com.theveloper.pixelplay.data.model.SortOption -import com.theveloper.pixelplay.data.model.FolderSource import com.theveloper.pixelplay.data.model.StorageFilter import com.theveloper.pixelplay.data.preferences.PlaylistPreferencesRepository import com.theveloper.pixelplay.data.preferences.UserPreferencesRepository @@ -56,33 +46,36 @@ import com.theveloper.pixelplay.utils.LogUtils import com.theveloper.pixelplay.utils.StorageType import com.theveloper.pixelplay.utils.StorageUtils import com.theveloper.pixelplay.utils.toHexString +import dagger.Lazy +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.io.File -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.PagingData -import androidx.paging.map -import androidx.paging.filter -import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.CoroutineScope +import javax.inject.Inject +import javax.inject.Singleton @OptIn(ExperimentalCoroutinesApi::class) @Singleton diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiMetadataSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiMetadataSheet.kt index 433ed3485..c4f96337c 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiMetadataSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiMetadataSheet.kt @@ -49,6 +49,7 @@ fun AiMetadataSheet( error: String?, onRetry: () -> Unit ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val colors = MaterialTheme.colorScheme diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiPlaylistSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiPlaylistSheet.kt index 1566a6678..4a9ca46c1 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiPlaylistSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/AiPlaylistSheet.kt @@ -84,6 +84,7 @@ fun AiPlaylistSheet( var minLength by remember { mutableStateOf("5") } var maxLength by remember { mutableStateOf("15") } + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, ) diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/AlbumMultiSelectionOptionSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/AlbumMultiSelectionOptionSheet.kt index 37cee5f1f..55bbc7035 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/AlbumMultiSelectionOptionSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/AlbumMultiSelectionOptionSheet.kt @@ -57,6 +57,7 @@ fun AlbumMultiSelectionOptionSheet( onPlayNext: () -> Unit, onAddToQueue: () -> Unit ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) ModalBottomSheet( diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/CastBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/CastBottomSheet.kt index e2a0a78d8..80a1673a4 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/CastBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/CastBottomSheet.kt @@ -333,6 +333,7 @@ fun CastBottomSheet( bluetoothName = activeBluetoothName ) + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, confirmValueChange = { true } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/CustomPresetsSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/CustomPresetsSheet.kt index ab8e7a63b..c09461091 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/CustomPresetsSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/CustomPresetsSheet.kt @@ -51,6 +51,7 @@ fun CustomPresetsSheet( onDelete: (EqualizerPreset) -> Unit, onDismiss: () -> Unit ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) ModalBottomSheet( diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/DailyMixMenu.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/DailyMixMenu.kt index f87192e7e..16ef74157 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/DailyMixMenu.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/DailyMixMenu.kt @@ -28,6 +28,7 @@ fun DailyMixMenu( onApplyPrompt: (String) -> Unit, isLoading: Boolean ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState() var prompt by remember { mutableStateOf("") } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/EditSongSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/EditSongSheet.kt index 6040ebc3a..dcf9ba755 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/EditSongSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/EditSongSheet.kt @@ -10,18 +10,15 @@ import androidx.compose.foundation.gestures.rememberTransformableState import androidx.compose.foundation.gestures.transformable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.rounded.Album import androidx.compose.material.icons.rounded.Category import androidx.compose.material.icons.rounded.FormatListNumbered import androidx.compose.material.icons.rounded.Info import androidx.compose.material.icons.rounded.Image import androidx.compose.material.icons.rounded.MusicNote -import androidx.compose.material.icons.rounded.Notes import androidx.compose.material.icons.rounded.Person import androidx.compose.material3.* import androidx.compose.material3.BasicAlertDialog @@ -72,16 +69,13 @@ import racra.compose.smooth_corner_rect_library.AbsoluteSmoothCornerShape import dev.shreyaspatil.capturable.capturable import androidx.compose.ui.geometry.Offset import androidx.compose.material.icons.rounded.Restore -import androidx.compose.material.icons.rounded.Shuffle -import androidx.compose.material.icons.rounded.Timer import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.focus.focusModifier import androidx.compose.ui.text.font.FontWeight +import androidx.core.net.toUri import kotlin.math.roundToInt import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties -import androidx.media3.common.Player import com.theveloper.pixelplay.data.media.AudioMetadataReader import com.theveloper.pixelplay.data.media.CoverArtUpdate import dev.shreyaspatil.capturable.controller.rememberCaptureController @@ -623,7 +617,7 @@ private fun EditSongContent( val encodedTitle = URLEncoder.encode(title, "UTF-8") val encodedArtist = URLEncoder.encode(artist, "UTF-8") val url = "https://lrclib.net/search/$encodedTitle%20$encodedArtist" - val intent = Intent(Intent.ACTION_VIEW).setData(Uri.parse(url)) + val intent = Intent(Intent.ACTION_VIEW).setData(url.toUri()) context.startActivity(intent) }, ) { @@ -862,9 +856,11 @@ fun CoverArtCropperDialog( var isLoading by remember { mutableStateOf(true) } var loadError by remember { mutableStateOf(null) } var isSaving by remember { mutableStateOf(false) } - var scale by remember { mutableStateOf(1f) } + var scale by remember { mutableFloatStateOf(1f) } var offset by remember { mutableStateOf(Offset.Zero) } - var containerSize by remember { mutableStateOf(0f) } + var containerSize by remember { mutableFloatStateOf(0f) } + + val errorMsg = stringResource(R.string.edit_song_unable_to_load_image) LaunchedEffect(sourceUri) { isLoading = true @@ -873,7 +869,7 @@ fun CoverArtCropperDialog( if (bitmap != null) { loadedBitmap = bitmap.asImageBitmap() } else { - loadError = context.getString(R.string.edit_song_unable_to_load_image) + loadError = errorMsg } isLoading = false scale = 1f @@ -886,7 +882,7 @@ fun CoverArtCropperDialog( } } - val transformableState = rememberTransformableState { zoomChange, panChange, _ -> + val transformableState = rememberTransformableState { _, zoomChange, panChange, _ -> val newScale = (scale * zoomChange).coerceIn(1f, 4f) scale = newScale loadedBitmap?.let { bitmap -> @@ -1039,26 +1035,21 @@ fun CoverArtCropperDialog( Button( enabled = canConfirm && !isSaving, onClick = { - if (!canConfirm) return@Button dialogScope.launch { isSaving = true val captured = captureController.captureAsync().await() - if (captured != null) { - val bytes = withContext(Dispatchers.IO) { - imageBitmapToJpeg(captured) - } - if (bytes != null) { - onConfirm( - CoverArtCropResult( - preview = captured, - update = CoverArtUpdate(bytes, COVER_ART_MIME_TYPE) - ) + val bytes = withContext(Dispatchers.IO) { + imageBitmapToJpeg(captured) + } + if (bytes != null) { + onConfirm( + CoverArtCropResult( + preview = captured, + update = CoverArtUpdate(bytes, COVER_ART_MIME_TYPE) ) - } else { - Timber.w("Failed to convert captured cover art to JPEG") - } + ) } else { - Timber.w("CaptureController returned null bitmap") + Timber.w("Failed to convert captured cover art to JPEG") } isSaving = false } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/GenreSortBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/GenreSortBottomSheet.kt index 47efe56f0..1641b8139 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/GenreSortBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/GenreSortBottomSheet.kt @@ -48,6 +48,7 @@ fun GenreSortBottomSheet( onShuffle: () -> Unit, headerContent: @Composable (() -> Unit)? = null ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true ) diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/LibrarySortBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/LibrarySortBottomSheet.kt index 5b2c33919..8370222d6 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/LibrarySortBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/LibrarySortBottomSheet.kt @@ -77,6 +77,7 @@ fun LibrarySortBottomSheet( sourceToggleContent: (@Composable () -> Unit)? = null, extraContent: (@Composable () -> Unit)? = null ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val selectedColor = MaterialTheme.colorScheme.secondaryContainer diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/LyricsSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/LyricsSheet.kt index 21bdf6273..d17f7ef96 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/LyricsSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/LyricsSheet.kt @@ -449,6 +449,7 @@ fun LyricsSheet( var immersiveMode by remember { mutableStateOf(false) } var lastInteractionTime by remember { mutableLongStateOf(System.currentTimeMillis()) } var showMoreSheet by remember { mutableStateOf(false) } + @Suppress("DEPRECATION") val moreSheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true ) diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/MultiSelectionBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/MultiSelectionBottomSheet.kt index 5fefda0b9..de8f99977 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/MultiSelectionBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/MultiSelectionBottomSheet.kt @@ -106,6 +106,7 @@ fun MultiSelectionBottomSheet( onBatchEdit: () -> Unit ) { val context = LocalContext.current + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) // Compute if all selected songs are liked diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistBottomSheet.kt index 3c0a0c5a5..ae12f8d27 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistBottomSheet.kt @@ -64,6 +64,7 @@ fun PlaylistBottomSheet( ) { var showCreatePlaylistDialog by remember { mutableStateOf(false) } + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, confirmValueChange = { true } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistMultiSelectionBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistMultiSelectionBottomSheet.kt index a9afbd594..eba556244 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistMultiSelectionBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/PlaylistMultiSelectionBottomSheet.kt @@ -84,6 +84,7 @@ fun PlaylistMultiSelectionBottomSheet( onMergeAll: () -> Unit, onShareAll: () -> Unit ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val evenCornerRadius = 26.dp val buttonShape = AbsoluteSmoothCornerShape( diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/ReorderTabsSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/ReorderTabsSheet.kt index a24bd9ace..3f1fadea3 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/ReorderTabsSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/ReorderTabsSheet.kt @@ -65,6 +65,7 @@ import sh.calvin.reorderable.rememberReorderableLazyListState import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow +@Suppress("DEPRECATION") @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun ReorderTabsSheet( @@ -106,6 +107,7 @@ fun ReorderTabsSheet( ) } + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val scope = rememberCoroutineScope() val listState = rememberLazyListState() diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/RoundedParallaxCarousell.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/RoundedParallaxCarousell.kt index 92445df96..3aa790286 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/RoundedParallaxCarousell.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/RoundedParallaxCarousell.kt @@ -576,7 +576,7 @@ private class CarouselItemModifierNode( } // --- limitar además al propio layer (seguro) - val layerBounds = Rect(0f, 0f, size.width.toFloat(), size.height.toFloat()) + val layerBounds = Rect(0f, 0f, size.width, size.height) val maskRect = Rect(left, top, right, bottom).intersect(layerBounds) // --- actualizar info para la máscara (para MaskScope, etc.) diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongInfoBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongInfoBottomSheet.kt index 566627f04..78bde78ce 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongInfoBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongInfoBottomSheet.kt @@ -307,6 +307,7 @@ fun SongInfoBottomSheet( ) } + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, confirmValueChange = { true } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongPickerBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongPickerBottomSheet.kt index 1c29515fd..9088da0de 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongPickerBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/SongPickerBottomSheet.kt @@ -103,6 +103,7 @@ fun SongPickerBottomSheet( onConfirm: (Set) -> Unit, playerViewModel: PlayerViewModel = hiltViewModel() ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val selectedSongIds = remember { mutableStateMapOf().apply { diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/TimerOptionsBottomSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/TimerOptionsBottomSheet.kt index e5aeb0abe..3b669ae5d 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/TimerOptionsBottomSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/TimerOptionsBottomSheet.kt @@ -75,6 +75,7 @@ fun TimerOptionsBottomSheet( var showCustomTimePicker by rememberSaveable { mutableStateOf(false) } val context = LocalContext.current var timerSliderPosition by remember { mutableStateOf(0f) } + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val isSwitchEnabled = isEndOfTrackTimerActive diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt index 0e84ec5b7..410020521 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/components/player/FullPlayerContent.kt @@ -978,6 +978,7 @@ fun FullPlayerContent( ) } + @Suppress("DEPRECATION") val artistPickerSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) if (showArtistPicker && currentSongArtists.isNotEmpty()) { PlayerArtistPickerBottomSheet( diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/HomeScreen.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/HomeScreen.kt index 7fba291fa..7787898aa 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/HomeScreen.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/HomeScreen.kt @@ -252,7 +252,9 @@ fun HomeScreen( var showBetaInfoBottomSheet by remember { mutableStateOf(false) } var showStreamingProviderSheet by remember { mutableStateOf(false) } var cleanInstallDisclaimerDismissedThisSession by rememberSaveable { mutableStateOf(false) } + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState() + @Suppress("DEPRECATION") val betaSheetState = rememberModalBottomSheetState() val scope = rememberCoroutineScope() LocalContext.current diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryMediaTabs.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryMediaTabs.kt index d3991a9b3..5c049f320 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryMediaTabs.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryMediaTabs.kt @@ -214,7 +214,7 @@ fun LibraryAlbumsTab( when { refreshState is LoadState.Error && albums.itemCount == 0 -> { - val error = (refreshState as LoadState.Error).error + val error = refreshState.error Box( modifier = Modifier .fillMaxSize() @@ -524,7 +524,7 @@ fun LibraryArtistsTab( when { refreshState is LoadState.Error && artists.itemCount == 0 -> { - val error = (refreshState as LoadState.Error).error + val error = refreshState.error Box( modifier = Modifier .fillMaxSize() diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryScreen.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryScreen.kt index 8a4ff0a67..2b5849bb6 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryScreen.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibraryScreen.kt @@ -2644,6 +2644,7 @@ private fun LibraryTabSwitcherSheet( onEditClick: () -> Unit, onDismiss: () -> Unit ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) ModalBottomSheet( diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibrarySongsTab.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibrarySongsTab.kt index 24e9b98f1..392410d4c 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibrarySongsTab.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/LibrarySongsTab.kt @@ -225,7 +225,7 @@ fun LibrarySongsTab( when { refreshState is LoadState.Error && songs.itemCount == 0 -> { - val error = (refreshState as LoadState.Error).error + val error = refreshState.error Box( modifier = Modifier .fillMaxSize() diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/MashupScreen.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/MashupScreen.kt index 19a7dd84f..7848406b1 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/MashupScreen.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/MashupScreen.kt @@ -66,6 +66,7 @@ fun MashupScreen( mashupViewModel: MashupViewModel = hiltViewModel() ) { val mashupUiState by mashupViewModel.uiState.collectAsStateWithLifecycle() + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState() val scope = rememberCoroutineScope() diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/PlaylistDetailScreen.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/PlaylistDetailScreen.kt index 778440c30..9f7666bea 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/PlaylistDetailScreen.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/PlaylistDetailScreen.kt @@ -696,6 +696,7 @@ fun PlaylistDetailScreen( ) } if (showPlaylistOptionsSheet && !isFolderPlaylist) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) ModalBottomSheet( diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/SettingsCategoryScreen.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/SettingsCategoryScreen.kt index e7bfaf99b..2a6f07699 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/screens/SettingsCategoryScreen.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/screens/SettingsCategoryScreen.kt @@ -276,6 +276,7 @@ fun SettingsCategoryScreen( var paletteBulkCompletedCount by remember { mutableStateOf(0) } var paletteBulkTotalCount by remember { mutableStateOf(0) } var paletteSongSearchQuery by remember { mutableStateOf("") } + @Suppress("DEPRECATION") val paletteRegenerateSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val isAnyPaletteRegenerateRunning = isPaletteRegenerateRunning || isPaletteBulkRegenerateRunning val filteredPaletteSongs = remember(paletteRegenerateTargets, paletteSongSearchQuery) { diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/channel/TelegramChannelSearchSheet.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/channel/TelegramChannelSearchSheet.kt index af55bacbf..8aafe856a 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/channel/TelegramChannelSearchSheet.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/channel/TelegramChannelSearchSheet.kt @@ -75,6 +75,7 @@ fun TelegramChannelSearchSheet( val isLoading by viewModel.isLoading.collectAsStateWithLifecycle() val statusMessage by viewModel.statusMessage.collectAsStateWithLifecycle() val isOnline by viewModel.isOnline.collectAsStateWithLifecycle() + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val focusRequester = remember { FocusRequester() } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/dashboard/TelegramDashboardScreen.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/dashboard/TelegramDashboardScreen.kt index a70f15746..6c9f446cb 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/dashboard/TelegramDashboardScreen.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/telegram/dashboard/TelegramDashboardScreen.kt @@ -703,6 +703,7 @@ private fun ChannelActionsBottomSheet( onSync: () -> Unit, onDelete: () -> Unit ) { + @Suppress("DEPRECATION") val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val publicChannelFallback = stringResource(R.string.presentation_batch_f_public_channel_fallback) val usernameLabel = remember(channel.username) { diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/AccountsViewModel.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/AccountsViewModel.kt index 354d92987..1f2ee30e6 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/AccountsViewModel.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/AccountsViewModel.kt @@ -114,12 +114,12 @@ class AccountsViewModel @Inject constructor( ) { it.toList() }, loggingOutServices ) { states, activeLogouts -> - val (telegramConnected, telegramChannelCount) = states[0] as Pair - val (gDriveConnected, gDriveFolderCount) = states[1] as Pair - val (neteaseConnected, neteasePlaylistCount) = states[2] as Pair - val (qqConnected, qqPlaylistCount) = states[3] as Pair - val (navidromeConnected, navidromePlaylistCount) = states[4] as Pair - val (jellyfinConnected, jellyfinPlaylistCount) = states[5] as Pair + val (telegramConnected, telegramChannelCount) = states[0] + val (gDriveConnected, gDriveFolderCount) = states[1] + val (neteaseConnected, neteasePlaylistCount) = states[2] + val (qqConnected, qqPlaylistCount) = states[3] + val (navidromeConnected, navidromePlaylistCount) = states[4] + val (jellyfinConnected, jellyfinPlaylistCount) = states[5] val connectedAccounts = buildList { if (telegramConnected) { diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastStateHolder.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastStateHolder.kt index a9633ec45..bd4b8a0cb 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastStateHolder.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastStateHolder.kt @@ -196,6 +196,7 @@ class CastStateHolder @Inject constructor( updateRoutes() syncSelectedRouteFromRouter(router) } + @Suppress("OVERRIDE_DEPRECATION") override fun onRouteSelected(router: MediaRouter, route: MediaRouter.RouteInfo) { updateRoutes() syncSelectedRouteFromRouter(router) diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastTransferStateHolder.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastTransferStateHolder.kt index 124740938..fafad7331 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastTransferStateHolder.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/CastTransferStateHolder.kt @@ -1027,13 +1027,14 @@ class CastTransferStateHolder @Inject constructor( ): Boolean = withContext(Dispatchers.IO) { var connection: HttpURLConnection? = null runCatching { - connection = (URL(endpoint).openConnection() as HttpURLConnection).apply { + val conn = (URL(endpoint).openConnection() as HttpURLConnection).apply { connectTimeout = 150 readTimeout = 150 instanceFollowRedirects = false requestMethod = method } - val code = connection?.responseCode ?: -1 + connection = conn + val code = conn.responseCode code in 200..299 }.getOrDefault(false).also { connection?.disconnect() diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ConnectivityStateHolder.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ConnectivityStateHolder.kt index f86f46be3..d86e8e32b 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ConnectivityStateHolder.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/ConnectivityStateHolder.kt @@ -329,9 +329,9 @@ class ConnectivityStateHolder @Inject constructor( device.type == android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP || device.type == android.media.AudioDeviceInfo.TYPE_BLUETOOTH_SCO ) { - val name = device.productName?.toString()?.trim().orEmpty() + val name = device.productName.toString().trim() if (name.isNotEmpty() && !isOwnBluetoothDeviceName(name, localDeviceNames)) { - val address = device.address?.trim().orEmpty().takeIf { it.isNotEmpty() } + val address = device.address.trim().takeIf { it.isNotEmpty() } val key = bluetoothDeviceKey(address, name) connectedDevices[key] = BluetoothAudioDeviceState( name = name, @@ -506,9 +506,12 @@ class ConnectivityStateHolder @Inject constructor( private fun readConnectedWifiSsid(): String? { if (!hasFineLocationPermission()) return null + @Suppress("DEPRECATION") val info = wifiManager?.connectionInfo ?: return null + @Suppress("DEPRECATION") if (info.supplicantState != android.net.wifi.SupplicantState.COMPLETED) return null + @Suppress("DEPRECATION") var ssid = info.ssid if (ssid.startsWith("\"") && ssid.endsWith("\"")) { ssid = ssid.substring(1, ssid.length - 1) diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/GenreDetailViewModel.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/GenreDetailViewModel.kt index 3c5c821e6..8c8818197 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/GenreDetailViewModel.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/GenreDetailViewModel.kt @@ -158,7 +158,7 @@ class GenreDetailViewModel @Inject constructor( val sections = buildDisplaySections(songs, SortOption.ARTIST) val flattened = flattenSections(sections, artistMap) - val sorted = songs.sortedBy { it.artist ?: "Unknown Artist" } + val sorted = songs.sortedBy { it.artist } ProcessingResult(genre, songs, sorted, sections, flattened) } @@ -195,8 +195,8 @@ class GenreDetailViewModel @Inject constructor( val sections = buildDisplaySections(currentState.songs, newSort) val flattened = flattenSections(sections, artistMap) val sorted = when (newSort) { - SortOption.ARTIST -> currentState.songs.sortedBy { it.artist ?: "Unknown Artist" } - SortOption.ALBUM -> currentState.songs.sortedBy { it.album ?: "Unknown Album" } + SortOption.ARTIST -> currentState.songs.sortedBy { it.artist } + SortOption.ALBUM -> currentState.songs.sortedBy { it.album } SortOption.TITLE -> currentState.songs.sortedBy { it.title } } Triple(sections, flattened, sorted) @@ -278,10 +278,10 @@ class GenreDetailViewModel @Inject constructor( private fun buildDisplaySections(songs: List, sort: SortOption): List { return when (sort) { SortOption.ARTIST -> { - val sorted = songs.sortedBy { it.artist ?: "Unknown Artist" } - val grouped = sorted.groupBy { it.artist ?: "Unknown Artist" } + val sorted = songs.sortedBy { it.artist } + val grouped = sorted.groupBy { it.artist } grouped.map { (artist, artistSongs) -> - val albums = artistSongs.groupBy { it.album ?: "Unknown Album" }.map { (albumName, albumSongs) -> + val albums = artistSongs.groupBy { it.album }.map { (albumName, albumSongs) -> val sortedAlbumSongs = albumSongs.sortedWith( compareBy { it.discNumber ?: 1 } .thenBy { if (it.trackNumber > 0) it.trackNumber else Int.MAX_VALUE } @@ -293,8 +293,8 @@ class GenreDetailViewModel @Inject constructor( } } SortOption.ALBUM -> { - val sorted = songs.sortedBy { it.album ?: "Unknown Album" } - val grouped = sorted.groupBy { it.album ?: "Unknown Album" } + val sorted = songs.sortedBy { it.album } + val grouped = sorted.groupBy { it.album } grouped.map { (album, albumSongs) -> val sortedAlbumSongs = albumSongs.sortedWith( compareBy { it.discNumber ?: 1 } diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlaybackStateHolder.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlaybackStateHolder.kt index ebcdf502f..1a49ff1a1 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlaybackStateHolder.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlaybackStateHolder.kt @@ -694,7 +694,7 @@ class PlaybackStateHolder @Inject constructor( if (hasMediaMismatch) { Timber.tag(TAG).v( "Skipping local progress tick due media mismatch (visible=%s, player=%s)", - visibleSong?.id, + visibleSong.id, currentMediaId ) delay(tickMs) diff --git a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlayerViewModel.kt b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlayerViewModel.kt index 10a95ae16..789ac1cdb 100644 --- a/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlayerViewModel.kt +++ b/app/src/main/java/com/theveloper/pixelplay/presentation/viewmodel/PlayerViewModel.kt @@ -4005,9 +4005,7 @@ class PlayerViewModel @Inject constructor( _toastEvents.emit( context.getString(R.string.player_share_zip_failed_format, error.localizedMessage ?: ""), ) - println( - "Failed to share: ${error.localizedMessage}" - ) + Timber.e(error, "Failed to share") } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 10a198aa9..8eea83d14 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -242,7 +242,7 @@ (Mixed values) (Optional - leave empty to skip) Successfully updated %d songs - Updated %d of %d songs. Some files could not be edited. + Updated %1$d of %2$d songs. Some files could not be edited. Failed to update songs diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 756bee62c..04d0c255c 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -1,12 +1,15 @@ - + + + + 127.0.0.1 localhost - \ No newline at end of file + diff --git a/gradle.properties b/gradle.properties index 3456658ac..123fd60fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,6 +3,7 @@ kotlin.code.style=official APP_VERSION_NAME=0.7.0-beta APP_VERSION_CODE=8 org.gradle.configuration-cache=true +org.gradle.caching=true # Adjusted to stay within 3GB limit and avoid thrashing # Increased heap to 4GB and Metaspace to 1024m to prevent memory exhaustion org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+ParallelRefParsingEnabled @@ -27,6 +28,9 @@ android.r8.strictFullModeForKeepRules=true android.builtInKotlin=true android.newDsl=true +android.nonTransitiveRClass=true +android.enableJetifier=false ksp.useKSP2=true +ksp.incremental=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 989d74387..3f915f096 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -282,3 +282,62 @@ android-test = { id = "com.android.test", version.ref = "agp" } baselineprofile = { id = "androidx.baselineprofile", version.ref = "baselineprofile" } android-library = { id = "com.android.library", version.ref = "agp" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } + +[bundles] +compose = [ + "androidx-ui", + "androidx-ui-graphics", + "androidx-ui-tooling-preview", + "androidx-compose-material3", + "androidx-material-icons-core", + "androidx-material-icons-extended", + "androidx-constraintlayout-compose", + "androidx-foundation", + "androidx-animation", + "androidx-ui-text-google-fonts" +] +lifecycle = [ + "androidx-lifecycle-runtime-ktx", + "androidx-lifecycle-runtime-compose", + "lifecycleprocess" +] +room = [ + "androidx-room-runtime", + "androidx-room-ktx", + "androidx-room-paging" +] +paging = [ + "androidx-paging-runtime", + "androidx-paging-compose" +] +media3 = [ + "androidx-media3-exoplayer", + "androidx-media3-ui", + "androidx-media3-session", + "androidx-media3-exoplayer-midi", + "androidx-media3-transformer" +] +hilt = [ + "hilt-android", + "androidx-hilt-navigation-compose", + "androidx-hilt-lifecycle-viewmodel-compose", + "androidx-hilt-work" +] +networking = [ + "retrofit", + "converter-gson", + "okhttp", + "logging-interceptor" +] +wear = [ + "wear-compose-material", + "wear-compose-material3", + "wear-compose-foundation", + "wear-compose-navigation" +] +horologist = [ + "horologist-compose-layout", + "horologist-media-ui", + "horologist-audio-ui", + "horologist-composables" +] diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts index 947f59d97..d1929ba0d 100644 --- a/wear/build.gradle.kts +++ b/wear/build.gradle.kts @@ -32,13 +32,14 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) - lint { - abortOnError = false - checkReleaseBuilds = false - } } } + lint { + abortOnError = false + checkReleaseBuilds = true + } + buildFeatures { compose = true buildConfig = true @@ -60,63 +61,40 @@ kotlin { dependencies { implementation(project(":shared")) - // Wear OS Compose - implementation(libs.wear.compose.material) - implementation(libs.wear.compose.material3) - implementation(libs.wear.compose.foundation) - implementation(libs.wear.compose.navigation) - - // Horologist - implementation(libs.horologist.compose.layout) - implementation(libs.horologist.media.ui) - implementation(libs.horologist.audio.ui) - implementation(libs.horologist.composables) + // Wear OS & Horologist + implementation(libs.bundles.wear) + implementation(libs.bundles.horologist) // Data Layer implementation(libs.play.services.wearable) implementation(libs.kotlinx.coroutines.play.services) - // Hilt - implementation(libs.hilt.android) + // DI & Navigation + implementation(libs.bundles.hilt) ksp(libs.hilt.android.compiler) - implementation(libs.androidx.hilt.navigation.compose) - implementation(libs.androidx.hilt.lifecycle.viewmodel.compose) + ksp(libs.androidx.hilt.compiler) - // Compose core - implementation(platform(libs.androidx.compose.bom)) + // AndroidX & Compose + implementation(libs.androidx.core.ktx) + implementation(libs.bundles.lifecycle) implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.foundation) - - // Serialization - implementation(libs.kotlinx.serialization.json) - - // Image loading - implementation(libs.coil.compose) - - // Logging - implementation(libs.timber) - - // Core - implementation(libs.androidx.core.ktx) - - // Lifecycle - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.lifecycle.runtime.compose) - - // Material icons for Wear implementation(libs.androidx.material.icons.core) implementation(libs.androidx.material.icons.extended) - // Room (local database for transferred songs) - implementation(libs.androidx.room.runtime) - implementation(libs.androidx.room.ktx) + // Storage & Media + implementation(libs.bundles.room) ksp(libs.androidx.room.compiler) - - // Media3 ExoPlayer (standalone local playback) implementation(libs.androidx.media3.exoplayer) implementation(libs.androidx.media3.session) implementation(libs.androidx.mediarouter) + // UI Utilities + implementation(libs.coil.compose) + implementation(libs.timber) + implementation(libs.kotlinx.serialization.json) + constraints { // Fix vulnerabilities in transitive dependencies implementation(libs.netty.common) From a82f000b6644f1a6de62f56816991cb7c2e6f398 Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Thu, 28 May 2026 17:30:50 -0500 Subject: [PATCH 04/15] Fix malformed baseline-prof.txt entry --- app/src/main/baseline-prof.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/baseline-prof.txt b/app/src/main/baseline-prof.txt index 4d909584f..8e7db006e 100644 --- a/app/src/main/baseline-prof.txt +++ b/app/src/main/baseline-prof.txt @@ -5997,7 +5997,7 @@ Landroidx/compose/material3/MinimumInteractiveModifierNode$$ExternalSyntheticLam SPLandroidx/compose/material3/MinimumInteractiveModifierNode$$ExternalSyntheticLambda0;->(ILandroidx/compose/ui/layout/Placeable;I)V PLandroidx/compose/material3/MinimumInteractiveModifierNode$$ExternalSyntheticLambda0;->invoke(Ljava/lang/Object;)Ljava/lang/Object; Landroidx/compose/material3/ModalBottomSheetKt; -HSPLandroidx/compose/material3/ModalBottomSheetKt;->rememberBottomSheetState(ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/Composer;Landroidx/compose/material3/SheetState; +HSPLandroidx/compose/material3/ModalBottomSheetKt;->rememberBottomSheetState(ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Landroidx/compose/material3/SheetState; Landroidx/compose/material3/ModalBottomSheetKt$$ExternalSyntheticLambda3; SPLandroidx/compose/material3/ModalBottomSheetKt$$ExternalSyntheticLambda3;->()V Landroidx/compose/material3/MotionScheme; From c818b10864c4659288b8cca1c5f822ae06722faa Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Thu, 28 May 2026 17:35:13 -0500 Subject: [PATCH 05/15] Fix CodeQL analysis failure by using autobuild --- .github/workflows/codeql.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index da9ec9033..084f682fb 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,10 +36,7 @@ jobs: uses: github/codeql-action/init@v4 with: languages: java-kotlin - build-mode: manual - - - name: Build with Gradle - run: ./gradlew assembleDebug # Use the wrapper for consistency + build-mode: autobuild - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 From 5fb5e23f72ee5e64875e55c20f13009e0e940b5e Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Thu, 28 May 2026 17:42:20 -0500 Subject: [PATCH 06/15] Fix CodeQL analysis: Use manual build and skip signing tasks --- .github/workflows/codeql.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 084f682fb..164274759 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,10 +36,12 @@ jobs: uses: github/codeql-action/init@v4 with: languages: java-kotlin - build-mode: autobuild + build-mode: manual + + - name: Build with Gradle + run: ./gradlew assembleDebug -x :app:validateSigningBenchmark -x :app:validateSigningRelease - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 with: - # Match the languages defined in the init step category: "/language:java-kotlin" From b8a99866c3f340f82fdda7af57b8859cd5805c0b Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Thu, 28 May 2026 17:45:39 -0500 Subject: [PATCH 07/15] Fix CodeQL: Disable cache and run clean build --- .github/workflows/codeql.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 164274759..af926bb46 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,6 +31,8 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v6 + with: + cache-disabled: true - name: Initialize CodeQL uses: github/codeql-action/init@v4 @@ -39,7 +41,7 @@ jobs: build-mode: manual - name: Build with Gradle - run: ./gradlew assembleDebug -x :app:validateSigningBenchmark -x :app:validateSigningRelease + run: ./gradlew clean assembleDebug -x :app:validateSigningBenchmark -x :app:validateSigningRelease - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 From 75089158e80675de75bb9f23067ca18c51dd0a97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 00:18:19 +0000 Subject: [PATCH 08/15] chore(deps): bump the gradle-dependencies group across 1 directory with 2 updates Bumps the gradle-dependencies group with 2 updates in the / directory: [com.google.genai:google-genai](https://github.com/googleapis/java-genai) and [io.mockk:mockk](https://github.com/mockk/mockk). Updates `com.google.genai:google-genai` from 1.55.0 to 1.56.0 - [Release notes](https://github.com/googleapis/java-genai/releases) - [Changelog](https://github.com/googleapis/java-genai/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-genai/compare/v1.55.0...v1.56.0) Updates `io.mockk:mockk` from 1.14.9 to 1.14.11 - [Release notes](https://github.com/mockk/mockk/releases) - [Commits](https://github.com/mockk/mockk/compare/1.14.9...v1.14.11) --- updated-dependencies: - dependency-name: com.google.genai:google-genai dependency-version: 1.56.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gradle-dependencies - dependency-name: io.mockk:mockk dependency-version: 1.14.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gradle-dependencies ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3f915f096..c9b84e4bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ accompanistDrawablepainter = "0.37.3" agp = "9.2.1" app = "1.7.0" -googleGenai = "1.55.0" +googleGenai = "1.56.0" googlePlayServicesCast = "22.3.1" animation = "1.11.2" appcompat = "1.7.1" @@ -89,7 +89,7 @@ workRuntimeKtx = "2.11.2" composeTesting = "1.0.0-alpha03" timber = "5.0.1" generativeai = "0.9.0" -mockk = "1.14.9" +mockk = "1.14.11" turbine = "1.2.1" truth = "1.4.5" From cb2de93d2716e6b405e4389debf39e4f5ac77204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=D0=B0=D0=B5=20=D0=95u=D0=BD=D1=88=D0=B0?= Date: Mon, 1 Jun 2026 22:54:00 -0500 Subject: [PATCH 09/15] Add CodeQL workflow for Java/Kotlin project analysis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dае Еuнша --- .github/workflows/codeql-build.yaml | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/codeql-build.yaml diff --git a/.github/workflows/codeql-build.yaml b/.github/workflows/codeql-build.yaml new file mode 100644 index 000000000..a5b5110f4 --- /dev/null +++ b/.github/workflows/codeql-build.yaml @@ -0,0 +1,58 @@ +name: "CodeQL - Build" + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: + schedule: + - cron: '25 15 * * 0' + +jobs: + build: + name: Build Database + if: github.actor != 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + cache: 'gradle' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v6 + with: + cache-read-only: false + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: java-kotlin + build-mode: manual + + - name: Build with Gradle + run: ./gradlew clean assembleDebug -x :app:validateSigningBenchmark -x :app:validateSigningRelease + + - name: Create CodeQL Database + uses: github/codeql-action/analyze@v4 + with: + category: "/language:java-kotlin" + skip-queries: true + upload: false + + - name: Upload CodeQL Database + uses: actions/upload-artifact@v4 + with: + name: codeql-database + path: ${{ runner.temp }}/codeql_databases + retention-days: 1 From 3fa12197809beca76b7d32e3dee844d0b3389d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=D0=B0=D0=B5=20=D0=95u=D0=BD=D1=88=D0=B0?= Date: Mon, 1 Jun 2026 22:54:24 -0500 Subject: [PATCH 10/15] Delete .github/workflows/codeql.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dае Еuнша --- .github/workflows/codeql.yml | 49 ------------------------------------ 1 file changed, 49 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index af926bb46..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: "CodeQL Advanced" - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - workflow_dispatch: - schedule: - - cron: '25 15 * * 0' - -jobs: - analyze: - name: Analyze - if: github.actor != 'dependabot[bot]' - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - steps: - - name: Checkout repository - uses: actions/checkout@v6 # v4 is current stable; v5 is emerging - - - name: Set up JDK 21 - uses: actions/setup-java@v5 - with: - distribution: 'temurin' - java-version: '21' - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v6 - with: - cache-disabled: true - - - name: Initialize CodeQL - uses: github/codeql-action/init@v4 - with: - languages: java-kotlin - build-mode: manual - - - name: Build with Gradle - run: ./gradlew clean assembleDebug -x :app:validateSigningBenchmark -x :app:validateSigningRelease - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 - with: - category: "/language:java-kotlin" From d29a9ae601b811c5efc95546a7a9de0d06016d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=D0=B0=D0=B5=20=D0=95u=D0=BD=D1=88=D0=B0?= Date: Mon, 1 Jun 2026 22:54:49 -0500 Subject: [PATCH 11/15] Add CodeQL security analysis workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dае Еuнша --- .github/workflows/codeql-security.yml | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/codeql-security.yml diff --git a/.github/workflows/codeql-security.yml b/.github/workflows/codeql-security.yml new file mode 100644 index 000000000..26f72cb8a --- /dev/null +++ b/.github/workflows/codeql-security.yml @@ -0,0 +1,34 @@ +name: "CodeQL - Security Analysis" + +on: + workflow_run: + workflows: ["CodeQL - Build"] + types: [completed] + workflow_dispatch: + +jobs: + analyze-security: + name: Security Analysis + if: github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Download CodeQL Database + uses: actions/download-artifact@v4 + with: + name: codeql-database + path: ${{ runner.temp }}/codeql_databases + run-id: ${{ github.event.workflow_run.id }} + + - name: Perform Security Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:java-kotlin/security" + db: ${{ runner.temp }}/codeql_databases/java-kotlin From bd4033a0e36909f6bfc45a7515b3e396d95d3533 Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Mon, 1 Jun 2026 22:59:03 -0500 Subject: [PATCH 12/15] chore: split-up CodeQL Workflows for cleanliness and lower failure rates due to time-outs or resource usages. --- .../{codeql-build.yaml => codeql-build.yml} | 0 .github/workflows/codeql-quality.yml | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+) rename .github/workflows/{codeql-build.yaml => codeql-build.yml} (100%) create mode 100644 .github/workflows/codeql-quality.yml diff --git a/.github/workflows/codeql-build.yaml b/.github/workflows/codeql-build.yml similarity index 100% rename from .github/workflows/codeql-build.yaml rename to .github/workflows/codeql-build.yml diff --git a/.github/workflows/codeql-quality.yml b/.github/workflows/codeql-quality.yml new file mode 100644 index 000000000..37ef80d75 --- /dev/null +++ b/.github/workflows/codeql-quality.yml @@ -0,0 +1,34 @@ +name: "CodeQL - Code Quality" + +on: + workflow_run: + workflows: ["CodeQL - Build"] + types: [completed] + workflow_dispatch: + +jobs: + analyze-quality: + name: Code Quality Analysis + if: github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Download CodeQL Database + uses: actions/download-artifact@v4 + with: + name: codeql-database + path: ${{ runner.temp }}/codeql_databases + run-id: ${{ github.event.workflow_run.id }} + + - name: Perform Code Quality Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:java-kotlin/quality" + db: ${{ runner.temp }}/codeql_databases/java-kotlin From 717175ed1965fa13c4caec2f5e897031bc9d5024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=D0=B0=D0=B5=20=D0=95u=D0=BD=D1=88=D0=B0?= Date: Mon, 1 Jun 2026 23:13:19 -0500 Subject: [PATCH 13/15] Update CodeQL database upload path for Java-Kotlin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dае Еuнша --- .github/workflows/codeql-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-build.yml b/.github/workflows/codeql-build.yml index a5b5110f4..53f9c6d0a 100644 --- a/.github/workflows/codeql-build.yml +++ b/.github/workflows/codeql-build.yml @@ -54,5 +54,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: codeql-database - path: ${{ runner.temp }}/codeql_databases + path: ${{ runner.temp }}/codeql_databases/java-kotlin retention-days: 1 From 24bba4fa31a9a29c9ce5c1ad54bf792c6311f1f1 Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Mon, 1 Jun 2026 23:24:50 -0500 Subject: [PATCH 14/15] refactor: consolidate CodeQL workflows and add linting/testing - Consolidate CodeQL build, security, and quality checks into single codeql.yml workflow - Fixes GitHub CodeQL recognition (now uses standard workflow naming) - Runs build and analysis inline, eliminating artifact passing overhead - Maintains aggressive caching for faster subsequent runs (~5-7 mins with cache vs 15+ mins) - Reduces resource usage by avoiding parallel CodeQL analysis jobs - Add lint.yml workflow for code quality checks - Add tests.yml workflow for unit and integration tests - All workflows use Gradle cache to minimize build times on dependency updates --- .github/workflows/codeql-quality.yml | 34 ------------------- .github/workflows/codeql-security.yml | 34 ------------------- .../{codeql-build.yml => codeql.yml} | 18 +++------- .github/workflows/lint.yml | 13 +++++++ .github/workflows/tests.yml | 13 +++++++ 5 files changed, 31 insertions(+), 81 deletions(-) delete mode 100644 .github/workflows/codeql-quality.yml delete mode 100644 .github/workflows/codeql-security.yml rename .github/workflows/{codeql-build.yml => codeql.yml} (74%) create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/codeql-quality.yml b/.github/workflows/codeql-quality.yml deleted file mode 100644 index 37ef80d75..000000000 --- a/.github/workflows/codeql-quality.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: "CodeQL - Code Quality" - -on: - workflow_run: - workflows: ["CodeQL - Build"] - types: [completed] - workflow_dispatch: - -jobs: - analyze-quality: - name: Code Quality Analysis - if: github.event.workflow_run.conclusion == 'success' - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Download CodeQL Database - uses: actions/download-artifact@v4 - with: - name: codeql-database - path: ${{ runner.temp }}/codeql_databases - run-id: ${{ github.event.workflow_run.id }} - - - name: Perform Code Quality Analysis - uses: github/codeql-action/analyze@v4 - with: - category: "/language:java-kotlin/quality" - db: ${{ runner.temp }}/codeql_databases/java-kotlin diff --git a/.github/workflows/codeql-security.yml b/.github/workflows/codeql-security.yml deleted file mode 100644 index 26f72cb8a..000000000 --- a/.github/workflows/codeql-security.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: "CodeQL - Security Analysis" - -on: - workflow_run: - workflows: ["CodeQL - Build"] - types: [completed] - workflow_dispatch: - -jobs: - analyze-security: - name: Security Analysis - if: github.event.workflow_run.conclusion == 'success' - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Download CodeQL Database - uses: actions/download-artifact@v4 - with: - name: codeql-database - path: ${{ runner.temp }}/codeql_databases - run-id: ${{ github.event.workflow_run.id }} - - - name: Perform Security Analysis - uses: github/codeql-action/analyze@v4 - with: - category: "/language:java-kotlin/security" - db: ${{ runner.temp }}/codeql_databases/java-kotlin diff --git a/.github/workflows/codeql-build.yml b/.github/workflows/codeql.yml similarity index 74% rename from .github/workflows/codeql-build.yml rename to .github/workflows/codeql.yml index 53f9c6d0a..fb14fae7f 100644 --- a/.github/workflows/codeql-build.yml +++ b/.github/workflows/codeql.yml @@ -1,4 +1,4 @@ -name: "CodeQL - Build" +name: "CodeQL" on: push: @@ -10,13 +10,14 @@ on: - cron: '25 15 * * 0' jobs: - build: - name: Build Database + analyze: + name: CodeQL Analysis if: github.actor != 'dependabot[bot]' runs-on: ubuntu-latest permissions: actions: read contents: read + security-events: write steps: - name: Checkout repository @@ -43,16 +44,7 @@ jobs: - name: Build with Gradle run: ./gradlew clean assembleDebug -x :app:validateSigningBenchmark -x :app:validateSigningRelease - - name: Create CodeQL Database + - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 with: category: "/language:java-kotlin" - skip-queries: true - upload: false - - - name: Upload CodeQL Database - uses: actions/upload-artifact@v4 - with: - name: codeql-database - path: ${{ runner.temp }}/codeql_databases/java-kotlin - retention-days: 1 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..1778e8632 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,13 @@ +name: "Lint" +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + cache: 'gradle' + - run: ./gradlew lint detekt diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..ec363a815 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,13 @@ +name: "Tests" +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + cache: 'gradle' + - run: ./gradlew test --parallel From 7c0dd566b7d00593eb5fe8d4115cec2877953b03 Mon Sep 17 00:00:00 2001 From: Dae Euhwa Date: Mon, 1 Jun 2026 23:28:46 -0500 Subject: [PATCH 15/15] CodeQL Permissions Corrections --- .github/workflows/codeql.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index fb14fae7f..96d8e64b8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,3 +48,4 @@ jobs: uses: github/codeql-action/analyze@v4 with: category: "/language:java-kotlin" + upload: true