Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
9e7c722
feat: use game hero image as booting splash background
xXJSONDeruloXx Apr 7, 2026
0ee5c10
fix: add drop shadow to booting splash status and tip text
xXJSONDeruloXx Apr 7, 2026
0fa26c9
feat: resolve hero image for all game sources on booting splash
xXJSONDeruloXx Apr 7, 2026
e6b8ed5
Update app/src/main/java/app/gamenative/ui/model/MainViewModel.kt
xXJSONDeruloXx Apr 8, 2026
9cd4973
Added verify files to GOG, clear prerequisite markers on verify, allo…
Apr 7, 2026
ace056d
fix: do not crash on game start on Meta Quest (#1105)
lvonasek Apr 7, 2026
830802e
Don't show logged out steam splash when offline and steam games are i…
utkarshdalal Apr 7, 2026
7457dee
Hide local saves only setting (#1139)
utkarshdalal Apr 7, 2026
f54215a
Added back best config for non steam games, except remove executable …
utkarshdalal Apr 8, 2026
99272f0
bumped version code for 0.9 full release
Apr 8, 2026
f7a6e83
fix: prevent carousel touch loss during pagination (#1127)
xXJSONDeruloXx Apr 8, 2026
20d55c2
fix: Download support files for gen 2 (#1130)
unbelievableflavour Apr 8, 2026
a04cff8
fix: include shared redistributables when resolving steam depots (#1166)
joshuatam Apr 11, 2026
b924cfc
fix: use horizontal tab row for downloads screen in portrait mode (#1…
xXJSONDeruloXx Apr 11, 2026
89524f5
Added wine env var fix type + fix for stardew (#1156)
unbelievableflavour Apr 11, 2026
785bf59
fix: improve download memory handling on CaseInsensitiveFileSystem (#…
joshuatam Apr 11, 2026
8d577c8
chore: re-order downloads & storage tabs (#1079)
xXJSONDeruloXx Apr 11, 2026
aed7bac
chore: add fexcore 2604 (#1170)
xXJSONDeruloXx Apr 12, 2026
c89df0e
feat: empty Wine/XDG trash on container shutdown (#1151)
jeremybernstein Apr 12, 2026
f7543e9
Feat: Update wine-mono version to 11.0.0 in installer scripts (#1133)
Catpotatos Apr 12, 2026
906ca6d
Added additional directory for xna msi (#1125)
unbelievableflavour Apr 12, 2026
f84c659
feat: warn-before-exit preference with double-back confirmation (#993)
jeremybernstein Apr 12, 2026
3ce7f08
Update README.md
utkarshdalal Apr 12, 2026
66ed67e
fix: match UFS save file globs case-insensitively (#1183)
AnikethanVA Apr 13, 2026
a96cb06
feat: ludashi style vivid screen effect (#1106)
xXJSONDeruloXx Apr 13, 2026
5354ba8
Add GOG force cloud save and related string updates. (#812)
unbelievableflavour Apr 13, 2026
dcf423b
fix: library view not updating after game uninstall (#956)
jeremybernstein Apr 13, 2026
e52b476
fix: correct steam game dlc licensing logic and enhance dlc display (…
joshuatam Apr 13, 2026
f642ab3
fix: suppress connection banner during initial Steam connect (#918)
jeremybernstein Apr 13, 2026
b03e610
Devil blade reboot utkarsh (#1198)
utkarshdalal Apr 13, 2026
6187e29
fix: Migrate GSE Saves to steam userdata, always upload userdata file…
joshuatam Apr 13, 2026
39a7431
Add reusable INI game fix for Imperivm (#1009)
Misazam Apr 13, 2026
096090a
feat: disable IME extract UI and centralize singleLine keyboard behav…
jeremybernstein Apr 13, 2026
474ac68
fix: preserve file timestamps during Steam Cloud download (#1199)
joshuatam Apr 13, 2026
008f9be
feat: added multi-controller support (#1047)
bllendev Apr 13, 2026
4d78acf
fix: use container language for install size estimate (#1054)
jeremybernstein Apr 13, 2026
bab9179
feat: FSR 1.0 & Scaling Modes (#1112)
xXJSONDeruloXx Apr 13, 2026
d880298
fix: clear stale container state on task swipe and app restart (#1136)
jeremybernstein Apr 13, 2026
ada8c06
fix: recognize unhandled UFS path types and fix save pattern parsing …
kiequoo Apr 13, 2026
286f15f
fix: perf hud fps fix for other wrappers (#1164)
xXJSONDeruloXx Apr 13, 2026
15f45de
Fix silent cloud save overwrite when sync cache is missing (#1169)
jeremybernstein Apr 13, 2026
d7fc58e
fixed steam intent launches, no need to check for offline mode
Apr 13, 2026
4df4e4a
Added option to toggle button hints bar (#811)
unbelievableflavour Apr 13, 2026
34d8e9d
fix crashes on migrateGSESavesToSteamUserdata (#1207)
joshuatam Apr 13, 2026
47001c4
GOG chunk URL broken when CDN token is in query string (#1215)
unbelievableflavour Apr 14, 2026
1cbf3d7
Update CONTRIBUTING.md to reduce ambiguity
utkarshdalal Apr 14, 2026
d20d7cd
Update pull_request_template.md to reduce ambiguity
utkarshdalal Apr 14, 2026
916fba8
Jb/streaming assembly utkarsh (#1219)
utkarshdalal Apr 14, 2026
70e9f1b
Aligned epic downloads to GOG to make it faster from eg India (#1220)
utkarshdalal Apr 14, 2026
89c95f6
Revert "feat: added multi-controller support (#1047)" (#1224)
utkarshdalal Apr 15, 2026
03856cf
Create ROADMAP.md
utkarshdalal Apr 16, 2026
faff15a
Update ROADMAP.md
utkarshdalal Apr 16, 2026
a7c35dd
tests: Added unit tests for new key parts of Gamenative (#1143)
unbelievableflavour Apr 16, 2026
1cf8fc0
Added test to keep canonical root at the correct location (#1144)
unbelievableflavour Apr 16, 2026
263711d
fix: preserve aspect-correct viewport for screen effects (#1213)
xXJSONDeruloXx Apr 16, 2026
1613eb6
Create pr-label-command.yml
utkarshdalal Apr 16, 2026
3872a94
Recommendation page (#1235)
utkarshdalal Apr 16, 2026
d40ddca
fix: skip spurious conflict when cache lost but local==remote (#1228)
jeremybernstein Apr 16, 2026
e24f275
chore(): openApi specs for Gog, Epic & Amazon (#1234)
phobos665 Apr 16, 2026
d7fd951
added toggle to disable posthog tracking (#1236)
utkarshdalal Apr 16, 2026
1e95f1a
refactor: Moved Proton downloading to launch deps (again, but mirrori…
unbelievableflavour Apr 16, 2026
aa9ab72
Added codeonwers file
Apr 17, 2026
375f34f
fix(): toggle showing achievements (#1251)
phobos665 Apr 20, 2026
d0a9992
fix: mouse movement in games that use raw mouse input and clipcursor …
AndreVto Apr 20, 2026
9ef4c95
More fallout game fixes (#1134)
unbelievableflavour Apr 20, 2026
f478706
fix: reject config with missing components, show dialog with apply-an…
jeremybernstein Apr 20, 2026
b94a86d
Added kingdom hearts 3 (EPIC) gamefix (#1161)
unbelievableflavour Apr 20, 2026
e6ddff8
feat: Add Steam save import and export actions (#966)
xXJSONDeruloXx Apr 20, 2026
cfa5d6d
Workshop Update: Manual Mod Folder Dialog (#1072)
Nightwalker743 Apr 20, 2026
fc1477c
feat: task manager from qam (#1121)
xXJSONDeruloXx Apr 20, 2026
5fb5288
fix/feat: extract XAudio DLLs from DirectX redistributables (#1184)
joshuatam Apr 20, 2026
7ceab01
fix: kill stale wine processes before launch (#1195)
xXJSONDeruloXx Apr 20, 2026
c4fd109
fix: GOG cloud save fetch failure handling (#1201)
kiequoo Apr 20, 2026
f89f2e3
feat: Parallelize Steam cloud save downloads (#1226)
kiequoo Apr 20, 2026
7decc54
fix: use stored installPath in GOGManager.deleteGame to prevent unins…
xXJSONDeruloXx Apr 20, 2026
a5c8157
added new turnip drivers to manifest (#1263)
utkarshdalal Apr 21, 2026
f1f4da9
chore(): add new box64 to GN. (#1262)
phobos665 Apr 21, 2026
d66f147
Revert "fix/feat: extract XAudio DLLs from DirectX redistributables (…
utkarshdalal Apr 21, 2026
aa714c0
Added some changes for rockstar launcher (#1274)
utkarshdalal Apr 22, 2026
00dafa0
Updated manifest to include new protons (#1275)
utkarshdalal Apr 22, 2026
86910ee
Added THIRD_PARTY_NOTICES
Apr 23, 2026
cb0725c
feat/refactor: Improve GOGDownloadManager download efficiency (#1277)
joshuatam Apr 23, 2026
fa3830e
fix: extract XAudio DLLs using native cabarc instead of 7-Zip binding…
joshuatam Apr 23, 2026
76c4930
fix(workshop): force standard Steam UGC path for Tale of Immortal (#1…
linkq Apr 23, 2026
6f1d621
Move Touchscreen Mode toggle to in-game sidebar (#1249)
Nightwalker743 Apr 23, 2026
a7e0e45
fix: late release single tap in touchscreen mode to fix clicking in s…
AndreVto Apr 23, 2026
0c064b3
Update README.md
utkarshdalal Apr 23, 2026
358cc05
feat(): Silly draft for ideation of EOS.
phobos665 Mar 8, 2026
dcdf42a
Added sidecar for EOS, made downloading EOS launch dependency, Delive…
Apr 24, 2026
fc3f78e
addressed AI comments
Apr 24, 2026
e982d73
fix: gog download pause / resume handling logic
joshuatam Apr 26, 2026
4a753d7
fix unit test
joshuatam Apr 26, 2026
38c71e6
revert downloadChunk logic on md5 checking before downloading file
joshuatam Apr 26, 2026
5a1d02d
Made layouts for appscreen and libraryscreen cutout/notch aware
Apr 26, 2026
498cf5a
Merge pull request #1300 from utkarshdalal/unpack-more
utkarshdalal Apr 26, 2026
084bece
fix: resolve cloud save path for Danganronpa 2 (app 413420) (#1297)
tlt21 Apr 27, 2026
8059e1b
fix/tabbar-scrolling-clip (#1293)
sdkahal Apr 27, 2026
cbaed0c
fix: copy a new dll generated with proton 11 (#1287)
AndreVto Apr 27, 2026
9eb9870
fix: open download details fullscreen (#1270)
xXJSONDeruloXx Apr 27, 2026
8bcd6f4
fix: repair steam save export (#1265)
xXJSONDeruloXx Apr 27, 2026
ffd6580
feat: move Disable Mouse Input to in-game Quick Menu overlay (#1267)
ben-pearson Apr 27, 2026
4470082
fix: join prealloc job before polling so zero-chunk files are created…
xXJSONDeruloXx Apr 28, 2026
2792bc6
fix: use consistent purple accent color for all quick menu items (#1311)
ben-pearson Apr 28, 2026
8457ead
Stop overwriting wine prefix when switching between containers of dif…
utkarshdalal Apr 28, 2026
67b9e04
merge: resolve conflicts with upstream/master
xXJSONDeruloXx Apr 28, 2026
c9c19e4
chore: merge upstream/master into feat/hero-bg-booting-splash
xXJSONDeruloXx May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/java/app/gamenative/ui/PluviaMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,7 @@ fun PluviaMain(
BootingSplash(
visible = state.showBootingSplash,
text = state.bootingSplashText,
heroImageUrl = state.bootingSplashHeroImageUrl,
)
}

Expand Down
155 changes: 80 additions & 75 deletions app/src/main/java/app/gamenative/ui/components/BootingSplash.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.gamenative.ui.theme.PluviaTheme
import app.gamenative.ui.theme.BrandGradient
import androidx.compose.ui.platform.LocalContext
import app.gamenative.R
import kotlin.math.sin
import app.gamenative.ui.theme.BrandGradient
import app.gamenative.ui.theme.PluviaTheme
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.coil.CoilImage
import kotlin.random.Random
import kotlinx.coroutines.delay

Expand All @@ -45,6 +49,7 @@ fun BootingSplash(
visible: Boolean = true,
text: String = "Initializing...",
progress: Float = -1f, // -1 for indeterminate, 0-1 for determinate
heroImageUrl: String = "",
) {
// Tips rotation (no animation cost, safe outside visibility check)
val context = LocalContext.current
Expand Down Expand Up @@ -124,31 +129,70 @@ fun BootingSplash(
label = "shimmer",
)

val particlePhase by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(20000, easing = LinearEasing),
repeatMode = RepeatMode.Restart,
),
label = "particlePhase",
)

Box(
modifier = Modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
colors = listOf(
MaterialTheme.colorScheme.background,
PluviaTheme.colors.surfacePanel,
MaterialTheme.colorScheme.background,
),
),
),
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
AmbientParticles(phase = particlePhase)
if (heroImageUrl.isNotEmpty()) {
CoilImage(
modifier = Modifier
.fillMaxSize()
.graphicsLayer {
scaleX = 1.06f
scaleY = 1.06f
}
.blur(3.dp),
imageModel = { heroImageUrl },
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
contentDescription = null,
),
loading = {},
failure = {},
previewPlaceholder = painterResource(R.drawable.ic_logo_color),
)
}

// Dark overlay — heavier than the carousel to keep text readable
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = if (heroImageUrl.isNotEmpty()) 0.55f else 0.85f)),
Comment thread
xXJSONDeruloXx marked this conversation as resolved.
)

// Vertical vignette (mirrors carousel backdrop)
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.verticalGradient(
colorStops = arrayOf(
0.0f to Color.Black.copy(alpha = 0.74f),
0.16f to Color.Black.copy(alpha = 0.52f),
0.38f to Color.Black.copy(alpha = 0.24f),
0.62f to Color.Black.copy(alpha = 0.34f),
1.0f to Color.Black.copy(alpha = 0.72f),
),
),
),
)

// Horizontal vignette (mirrors carousel backdrop)
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.horizontalGradient(
colorStops = arrayOf(
0.0f to Color.Black.copy(alpha = 0.34f),
0.14f to Color.Black.copy(alpha = 0.16f),
0.5f to Color.Transparent,
0.86f to Color.Black.copy(alpha = 0.16f),
1.0f to Color.Black.copy(alpha = 0.34f),
),
),
),
)

// Main content
Column(
Expand Down Expand Up @@ -213,6 +257,11 @@ fun BootingSplash(
style = MaterialTheme.typography.bodyMedium.copy(
fontWeight = FontWeight.Medium,
letterSpacing = 1.sp,
shadow = Shadow(
color = Color.Black.copy(alpha = 0.8f),
offset = Offset(0f, 1f),
blurRadius = 6f,
),
),
color = Color.White.copy(alpha = 0.7f),
textAlign = TextAlign.Center,
Expand All @@ -237,6 +286,11 @@ fun BootingSplash(
text = tips[idx],
style = MaterialTheme.typography.bodySmall.copy(
lineHeight = 20.sp,
shadow = Shadow(
color = Color.Black.copy(alpha = 0.8f),
offset = Offset(0f, 1f),
blurRadius = 6f,
),
),
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
Expand Down Expand Up @@ -306,55 +360,6 @@ private fun ProgressBar(
}
}

@Composable
private fun AmbientParticles(
phase: Float,
modifier: Modifier = Modifier,
) {
val particleColor = PluviaTheme.colors.accentCyan

val particles = remember {
List(12) {
ParticleData(
baseX = Random.nextFloat(),
baseY = Random.nextFloat(),
size = Random.nextFloat() * 3f + 1f,
speed = Random.nextFloat() * 0.5f + 0.5f,
phaseOffset = Random.nextFloat() * 360f,
)
}
}

Canvas(modifier = modifier.fillMaxSize()) {
particles.forEach { particle ->
val animatedPhase = (phase + particle.phaseOffset) * particle.speed
val radians = Math.toRadians(animatedPhase.toDouble())

val offsetX = (sin(radians) * 30).toFloat()
val offsetY = (sin(radians * 0.7) * 20).toFloat()

val x = particle.baseX * size.width + offsetX
val y = particle.baseY * size.height + offsetY

// Pulsing alpha based on phase
val alpha = (0.15f + 0.15f * sin(radians * 2).toFloat()).coerceIn(0f, 0.3f)

drawCircle(
color = particleColor.copy(alpha = alpha),
radius = particle.size.dp.toPx(),
center = Offset(x, y),
)
}
}
}

private data class ParticleData(
val baseX: Float,
val baseY: Float,
val size: Float,
val speed: Float,
val phaseOffset: Float,
)

@Preview(name = "BootingSplash - Indeterminate")
@Composable
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/app/gamenative/ui/data/MainState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ data class MainState(
val testGraphics: Boolean = false,
val showBootingSplash: Boolean = false,
val bootingSplashText: String = "Booting...",
val bootingSplashHeroImageUrl: String = "",

// Connection state for background reconnection
// Default to DISCONNECTED - service will start and set to CONNECTING
Expand Down
45 changes: 45 additions & 0 deletions app/src/main/java/app/gamenative/ui/model/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import app.gamenative.events.SteamEvent
import app.gamenative.ui.enums.Orientation
import java.util.EnumSet
import app.gamenative.service.SteamService
import app.gamenative.service.amazon.AmazonService
import app.gamenative.service.epic.EpicCloudSavesManager
import app.gamenative.service.epic.EpicService
import app.gamenative.service.gog.GOGService
import app.gamenative.utils.CustomGameScanner
import app.gamenative.ui.data.MainState
import app.gamenative.ui.enums.ConnectionState
import app.gamenative.ui.screen.PluviaScreen
Expand Down Expand Up @@ -318,6 +322,10 @@ class MainViewModel @Inject constructor(
_state.update { it.copy(bootingSplashText = value) }
}

fun setBootingSplashHeroImageUrl(url: String) {
_state.update { it.copy(bootingSplashHeroImageUrl = url) }
}

// Connection state management

/**
Expand Down Expand Up @@ -448,6 +456,43 @@ class MainViewModel @Inject constructor(
fun launchApp(context: Context, appId: String) {
// Show booting splash before launching the app
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) {
GameSource.STEAM -> {
val steamApp = SteamService.getAppInfoOf(gameId)
steamApp?.getHeroUrl()?.ifEmpty { steamApp.headerUrl } ?: ""
}
GameSource.GOG -> {
val game = GOGService.getGOGGameOf(gameId.toString())
game?.backgroundUrl?.ifEmpty { game.imageUrl } ?: ""
}
GameSource.EPIC -> {
val game = EpicService.getEpicGameOf(gameId)
game?.artPortrait?.ifEmpty { game.artCover.ifEmpty { game.artSquare } } ?: ""
}
GameSource.AMAZON -> {
val game = AmazonService.getAmazonGameByAppId(gameId)
game?.heroUrl?.ifEmpty { game.artUrl } ?: ""
}
GameSource.CUSTOM_GAME -> {
val folderPath = CustomGameScanner.getFolderPathFromAppId(appId)
if (folderPath != null) {
val folder = java.io.File(folderPath)
val heroFile = folder.listFiles()?.firstOrNull { file ->
Comment thread
xXJSONDeruloXx marked this conversation as resolved.
Comment thread
xXJSONDeruloXx marked this conversation as resolved.
file.isFile &&
file.name.startsWith("steamgriddb_hero", ignoreCase = true) &&
!file.name.contains("grid_", ignoreCase = true) &&
(file.name.endsWith(".png", ignoreCase = true) ||
file.name.endsWith(".jpg", ignoreCase = true) ||
file.name.endsWith(".webp", ignoreCase = true))
}
heroFile?.let { android.net.Uri.fromFile(it).toString() } ?: ""
} else ""
}
Comment thread
xXJSONDeruloXx marked this conversation as resolved.
}
setBootingSplashHeroImageUrl(heroUrl)
setShowBootingSplash(true)
PluviaApp.events.emit(AndroidEvent.SetAllowedOrientation(PrefManager.allowedOrientation))

Expand Down
Loading