feat: use game hero image as booting splash background#1128
feat: use game hero image as booting splash background#1128xXJSONDeruloXx wants to merge 106 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughSupplies a boot-splash hero image URL through MainState/MainViewModel and passes it into BootingSplash; BootingSplash renders a remote/local hero backdrop (with scale/blur, darkening and vignette overlays, and text drop shadows) when the URL is present and removes the prior particle canvas background. ChangesBoot splash feature (state, VM resolution, UI rendering)
Sequence Diagram(s)sequenceDiagram
participant VM as MainViewModel
participant Services as Remote Services
participant Scanner as CustomGameScanner
participant State as MainState
participant UI as BootingSplash
VM->>VM: launchApp(context, appId)
VM->>VM: parse gameSource & gameId
alt remote source (Steam/GOG/EPIC/AMAZON)
VM->>Services: request artwork fields / pick fallback URL
Services-->>VM: hero image URL or empty
else custom game
VM->>Scanner: find first local hero image file
Scanner-->>VM: file Uri string or empty
end
VM->>State: setBootingSplashHeroImageUrl(url)
VM->>State: setShowBootingSplash(true)
State-->>UI: expose heroImageUrl
alt heroImageUrl non-empty
UI->>UI: load image via Coil, apply scale & blur
UI->>UI: render overlays (darken, vertical/horizontal vignettes) and text shadows
else heroImageUrl empty
UI->>UI: render standard animated splash (particles/gradient)
end
UI-->>UI: display splash with progress/other animations
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
phobos665
left a comment
There was a problem hiding this comment.
Based on the video and code, this looks like a massive improvement in both style and legibility.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/ui/model/MainViewModel.kt (1)
458-491: Consider moving I/O operations off the main thread.The hero URL resolution runs on the main dispatcher. While most service lookups are likely cached in memory, the custom game path involves
folder.listFiles()which is a filesystem I/O operation. Consider wrapping the custom game branch (or the entire resolution block) inwithContext(Dispatchers.IO)to avoid potential UI jank.♻️ Optional: Move resolution to IO dispatcher
viewModelScope.launch { // Resolve hero image URL for the booting splash background val gameSource = ContainerUtils.extractGameSourceFromContainerId(appId) val gameId = ContainerUtils.extractGameIdFromContainerId(appId) - val heroUrl = when (gameSource) { + val heroUrl = withContext(Dispatchers.IO) { + when (gameSource) { // ... existing cases ... + } + } setBootingSplashHeroImageUrl(heroUrl)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/app/gamenative/ui/model/MainViewModel.kt` around lines 458 - 491, The hero URL resolution in MainViewModel does filesystem I/O on the main dispatcher (notably CustomGameScanner.getFolderPathFromAppId and folder.listFiles()), so move the custom-game branch (or the whole resolution block) into a withContext(Dispatchers.IO) block to perform folder.listFiles() off the main thread; update the coroutine context where heroUrl is computed to call withContext(Dispatchers.IO) and keep the UI-thread-safe return values (strings/URIs) to be used back on the main dispatcher.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/app/gamenative/ui/model/MainViewModel.kt`:
- Around line 478-490: The current custom-game hero lookup in the
GameSource.CUSTOM_GAME branch uses startsWith("steamgriddb_grid_hero") and thus
incorrectly selects grid images; update the filter in the heroFile selection
(the lambda used when assigning heroFile) to match files starting with
"steamgriddb_hero" and explicitly exclude any names containing "grid_" (i.e.,
startsWith("steamgriddb_hero", ignoreCase = true) &&
!file.name.contains("grid_", ignoreCase = true)) while keeping the existing
extension checks; this change should be made near
CustomGameScanner.getFolderPathFromAppId usage where heroFile is determined.
---
Nitpick comments:
In `@app/src/main/java/app/gamenative/ui/model/MainViewModel.kt`:
- Around line 458-491: The hero URL resolution in MainViewModel does filesystem
I/O on the main dispatcher (notably CustomGameScanner.getFolderPathFromAppId and
folder.listFiles()), so move the custom-game branch (or the whole resolution
block) into a withContext(Dispatchers.IO) block to perform folder.listFiles()
off the main thread; update the coroutine context where heroUrl is computed to
call withContext(Dispatchers.IO) and keep the UI-thread-safe return values
(strings/URIs) to be used back on the main dispatcher.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 30d481cd-96a4-4940-8449-eb940a2e8962
📒 Files selected for processing (4)
app/src/main/java/app/gamenative/ui/PluviaMain.ktapp/src/main/java/app/gamenative/ui/components/BootingSplash.ktapp/src/main/java/app/gamenative/ui/data/MainState.ktapp/src/main/java/app/gamenative/ui/model/MainViewModel.kt
There was a problem hiding this comment.
2 issues found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/src/main/java/app/gamenative/ui/components/BootingSplash.kt">
<violation number="1" location="app/src/main/java/app/gamenative/ui/components/BootingSplash.kt:158">
P2: Newly added hardcoded overlay/vignette/shadow colors bypass centralized theme resources, reducing theming consistency and maintainability.</violation>
</file>
<file name="app/src/main/java/app/gamenative/ui/model/MainViewModel.kt">
<violation number="1" location="app/src/main/java/app/gamenative/ui/model/MainViewModel.kt:482">
P2: Synchronous directory scan (`File.listFiles`) runs inside `viewModelScope.launch` on the main dispatcher, which can block UI/ANR for large custom-game folders. Offload this filesystem scan to `Dispatchers.IO` or a helper that limits/streams results.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…w submitting feedback for non-steam, non-custom games
…nstalled (utkarshdalal#1138) Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
…path if it's a different store (utkarshdalal#1141) Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
* fix: prevent carousel touch loss during pagination by removing touch-conflicting draggable modifier * fix: address bot review feedback on carousel mouse input handler * fix: add drag slop to carousel mouse drag to prevent accidental scrolls on click
* Download support files for gen 2 * Added tests to verify downloading support and game files for gen 1 and 2
…arshdalal#1166) * fix: include shared redistributables when resolving steam depots Merges depot IDs from the shared license (ID 0) with app-specific depots to ensure common redistributables and dependencies are correctly identified and downloaded along with game-specific files. * Update app/src/main/java/app/gamenative/service/SteamService.kt Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
…tkarshdalal#1142) * do less caching for god of war download * bump js version 1.8.0.1-18-SNAPSHOT, update CaseInsensitiveFileSystem to implement BaseCaseInsensitiveFileSystem * only remove the file from cache inside removeFileCache * fix: nested segment cache + pre-populated listings in CaseInsensitiveFileSystem * update resolveAndCache only cache directory and skip file * add log option to CaseInsensitiveFileSystem, handle deleteRecursively properly * coderabbitai comments --------- Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com> Co-authored-by: Jeremy Bernstein <jeremy.d.bernstein@googlemail.com>
* Reorder sidebar: Storage Manager first, Downloads second; fix header title on Downloads tab * Default selected tab to Storage Manager * Add downloads_section_title translations for all 13 locales * fix: swap Settings before Downloads in System sidebar
Co-authored-by: Phobos665 <5970062+phobos665@users.noreply.github.com>
…hdalal#1133) * feat: update wine-mono version to 11.0.0 in installer scripts * fix: bump_version in ImageFsInstaller.java, will trigger extraction for all existing containers that are on Mono 9.0.0 and install 11.0.0.
…hdalal#1277) * refactor: Improve GOGDownloadManager download efficiency 1. Adopt Flow queuing concept from JavaSteam 2. add DownloadSpeedConfig to be used later for other store enhancement 3. update kotlinx-coroutines-core version to match JavaSteam version using 4. Update NetworkUtils httpForParallelDownloads for timeout and http protocol config * ai comments * add retry logic when same pendingChunks appear 10 times in a row
…utkarshdalal#1269) * Reapply "fix/feat: extract XAudio DLLs from DirectX redistributables (utkarshdalal#1184)" (utkarshdalal#1266) This reverts commit 93793fa. * fix: extract XAudio DLLs using native cabarc instead of 7-Zip binding Replaces the 7-Zip JBinding library with a Wine-based batch script that uses the native cabarc utility to extract DirectX DLLs. This removes the dependency on JitPack and the external 7-Zip library while maintaining the fix for Proton 10. * ai comments * ai comments * ai comments, refactor to XAudioUtils * guard batchCommand with BATCH_SUCCESS_CHECK * move guard code location after all extraction * remove useless comment * add splash text when extracting dlls
…karshdalal#1260) - Add AppID 1468810 (Tale of Immortal) to `forceStandardAppIds` to ensure workshop items are handled via the standard ISteamUGC path.
Moved Touchscreen Mode toggle from Edit Container > Controller section to the in-game sidebar, right below the existing Edit On-screen Controller line. Added gear icon for gesture settings that appears when touchscreen mode is active.
…ome games (utkarshdalal#1212) * fix: late release single tap in touchscreen mode to fix clicking in some games * fix: immediately release click before a new single tap * clear delayedPress state after execution * flush pending single tap release on handleTsDown
…r at all Costs working
made unpack files more aggressive
…alal#1297) Danganronpa 2 stores saves in WinMyDocuments/My Games/Danganronpa2/ via a Windows rootoverride, but GameNative was placing downloaded cloud saves in the game install directory instead, so the game never found them. Two bugs fixed: 1. KeyValueUtils: treat PICS path '/' as empty (same as '.') A lone forward-slash means 'root of this path type' with no subdir. Keeping the literal '/' caused uploadPath='/' which put a trailing slash on the cloudPrefixToLocalPath map key ('%GameInstall%/') while the lookup trimmed it ('%GameInstall%'), causing a miss. With the fix, uploadPath='' and path='My Games/Danganronpa2' (no trailing slash). Also bumps CURRENT_UFS_PARSE_VERSION 2->3 to force cache refresh. 2. SteamAutoCloud getFullFilePath: consult cloudPrefixToLocalPath when Steam embeds the placeholder in the filename (prefix=[], filename='%GameInstall%savedata.vfs'). The previous early-return hardcoded the destination as the game install dir, bypassing all rootoverride remapping. Now checks cloudPrefixToLocalPath['%GameInstall%'] first so the file lands in WinMyDocuments/My Games/Danganronpa2/. Also adds .trimEnd('/') to cloudKey construction in cloudPrefixToLocalPath so map keys are always slash-free (matching the existing lookup behaviour). Tests added: - KeyValueUtilsTest: danganronpa2SlashPathWithWindowsRootOverrideIsNormalizedToEmpty - SteamAutoCloudTest: downloadWithEmbeddedGameInstallPrefixUsesRootoverrideLocalPath - keyvalues/Danganronpa 2 Goodbye Despair.txt: PICS reference documentation
…dalal#1267) * feat: move Disable Mouse Input to in-game Quick Menu overlay Removes the Disable Mouse Input toggle from the container settings Controller tab and adds it to the in-game Quick Menu, where it can be toggled during gameplay without leaving the game. State is persisted to the container config on toggle. * fix: address code review feedback - Remove unconditional setCursorVisible call; cursor state is managed by existing pointer-availability logic and should not be overridden when a physical mouse may be present - Key isDisableMouseInput state to container.id to prevent stale state if container changes * fix: restore cursor visibility on mouse input toggle Show cursor when re-enabling mouse input (unless touchscreen mode is also active), hide it when disabling. Required for touch-as-mouse users who have no physical pointer device. * fix: use mouse icon and pink accent for Disable Mouse quick menu item
utkarshdalal#1310) * Stop overwriting wine prefix when switching between containers of different variants/archs, only extract xaudio dlls once * do reinstall of mono and preinstallsteps on wine version change (not arch), fixed bugs with wincomponents, audio driver, and startup selection being overwritten on wine version change --------- Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
There was a problem hiding this comment.
4 issues found across 202 files (changes from recent commits).
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritises the most important files to review.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/src/main/java/app/gamenative/gamefixes/IniFileFix.kt">
<violation number="1" location="app/src/main/java/app/gamenative/gamefixes/IniFileFix.kt:13">
P2: Regex replacement uses raw value, so `$`/`\` in INI defaults are interpreted as group refs/escapes and can corrupt or fail updates</violation>
</file>
<file name="CONTRIBUTING.md">
<violation number="1" location="CONTRIBUTING.md:27">
P2: Process section mandates pre-PR discussion/approval while Quick Start treats it as optional, creating conflicting contribution requirements.</violation>
</file>
<file name="app/src/main/java/app/gamenative/ui/util/ContainerConfigTransfer.kt">
<violation number="1" location="app/src/main/java/app/gamenative/ui/util/ContainerConfigTransfer.kt:108">
P2: Detached background coroutine returns false before import completes, breaking importConfig result/cancellation contract</violation>
</file>
<file name="app/src/main/java/app/gamenative/PluviaApp.kt">
<violation number="1" location="app/src/main/java/app/gamenative/PluviaApp.kt:231">
P2: `shutdownEnvironment()` does not clear the companion `xServerView`, so the new shared teardown path can retain a static Android view and its context, risking leaks/stale state despite being described as a full teardown.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| private fun updateIniValue(content: String, key: String, value: String): String { | ||
| val regex = Regex("(?im)^(${Regex.escape(key)}\\s*=\\s*).*$") | ||
| return if (regex.containsMatchIn(content)) { | ||
| content.replace(regex, "$1$value") |
There was a problem hiding this comment.
P2: Regex replacement uses raw value, so $/\ in INI defaults are interpreted as group refs/escapes and can corrupt or fail updates
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/gamefixes/IniFileFix.kt, line 13:
<comment>Regex replacement uses raw value, so `$`/`\` in INI defaults are interpreted as group refs/escapes and can corrupt or fail updates</comment>
<file context>
@@ -0,0 +1,66 @@
+private fun updateIniValue(content: String, key: String, value: String): String {
+ val regex = Regex("(?im)^(${Regex.escape(key)}\\s*=\\s*).*$")
+ return if (regex.containsMatchIn(content)) {
+ content.replace(regex, "$1$value")
+ } else {
+ val suffix = if (content.endsWith("\n") || content.isEmpty()) "" else System.lineSeparator()
</file context>
|
|
||
| ## Process | ||
|
|
||
| Before opening a PR: |
There was a problem hiding this comment.
P2: Process section mandates pre-PR discussion/approval while Quick Start treats it as optional, creating conflicting contribution requirements.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CONTRIBUTING.md, line 27:
<comment>Process section mandates pre-PR discussion/approval while Quick Start treats it as optional, creating conflicting contribution requirements.</comment>
<file context>
@@ -9,6 +9,32 @@ Thanks for your interest in contributing. We welcome pull requests, bug reports,
+
+## Process
+
+Before opening a PR:
+1. Discuss the change in #code-changes
+2. Wait for it to be acknowledged/approved
</file context>
| if (missingComponents.isNotEmpty()) { | ||
| BaseAppScreen.showMissingComponentsDialog(appId, missingComponents) { | ||
| // "apply anyway" — re-parse with defaults, install manifest entries, apply | ||
| CoroutineScope(Dispatchers.IO).launch { |
There was a problem hiding this comment.
P2: Detached background coroutine returns false before import completes, breaking importConfig result/cancellation contract
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/ui/util/ContainerConfigTransfer.kt, line 108:
<comment>Detached background coroutine returns false before import completes, breaking importConfig result/cancellation contract</comment>
<file context>
@@ -97,10 +100,55 @@ object ContainerConfigTransfer {
+ if (missingComponents.isNotEmpty()) {
+ BaseAppScreen.showMissingComponentsDialog(appId, missingComponents) {
+ // "apply anyway" — re-parse with defaults, install manifest entries, apply
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ val forced = BestConfigService.parseConfigToContainerData(
</file context>
| runCatching { env?.stopEnvironmentComponents() } | ||
| .onFailure { Timber.e(it, "shutdownEnvironment: stopEnvironmentComponents") } | ||
|
|
||
| xEnvironment = null |
There was a problem hiding this comment.
P2: shutdownEnvironment() does not clear the companion xServerView, so the new shared teardown path can retain a static Android view and its context, risking leaks/stale state despite being described as a full teardown.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/PluviaApp.kt, line 231:
<comment>`shutdownEnvironment()` does not clear the companion `xServerView`, so the new shared teardown path can retain a static Android view and its context, risking leaks/stale state despite being described as a full teardown.</comment>
<file context>
@@ -200,6 +210,33 @@ class PluviaApp : SplitCompatApplication() {
+ runCatching { env?.stopEnvironmentComponents() }
+ .onFailure { Timber.e(it, "shutdownEnvironment: stopEnvironmentComponents") }
+
+ xEnvironment = null
+ inputControlsView = null
+ inputControlsManager = null
</file context>
| xEnvironment = null | |
| xEnvironment = null | |
| xServerView = null |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/app/gamenative/ui/model/MainViewModel.kt`:
- Around line 458-482: The hero URL resolution in launchApp is doing blocking IO
and service lookups on the Main dispatcher (calls like
CustomGameScanner.getFolderPathFromAppId, File.listFiles(),
SteamService.getAppInfoOf, GOGService.getGOGGameOf, EpicService.getEpicGameOf,
AmazonService.getAmazonGameByAppId), which delays setShowBootingSplash(true);
move the entire hero-resolution block into withContext(Dispatchers.IO) so it
runs off the main thread, return the resolved heroUrl back to the caller thread,
and keep UI state calls (e.g., setShowBootingSplash(true)) on the Main
dispatcher; ensure you only access UI/state from the Main dispatcher after the
IO work completes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2eecf4e7-6aec-4c61-8b78-f94b55be8a50
📒 Files selected for processing (2)
app/src/main/java/app/gamenative/ui/PluviaMain.ktapp/src/main/java/app/gamenative/ui/model/MainViewModel.kt
|
I disagree, I think these look great, greyscale would be a bummer |



Description
add game art from hero or equivalent when game is booting, with drop shadow blur and darkening to make text more legible. more closely mirrors steam bpm experience and personalizes per game.
https://discord.com/channels/1378308569287622737/1489699856774729839
Recording
Screen.Recording.2026-04-07.at.10.37.22.AM.mov
Checklist
#code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.CONTRIBUTING.md.Summary by cubic
Use each game's hero image as the booting splash background to personalize launch and improve text readability. Adds blur, vignettes, and a dark overlay; resolves hero art across stores and local custom games.
BootingSplashacceptsheroImageUrland renders a blurred, full-bleed background with vertical/horizontal vignettes and a darker scrim; adds drop shadows to status/tip text and removes background particles.MainViewModelpicks hero art with fallbacks (Steam hero→header, GOG background→image, Epic portrait→cover→square, Amazon hero→art, localsteamgriddb_hero*); wired viaMainState.bootingSplashHeroImageUrland passed throughPluviaMain.Written for commit c9c19e4. Summary will update on new commits.
Summary by CodeRabbit
New Features
Style