settings: open faster by building the sections over a few frames, and keep scrolling smooth

This commit is contained in:
Seb3thehacker committed 2026-10-25 14:39:13 -07:00
1 parent 5cc1f518c7
commit 5c72a18f06
1 file changed
+24 -10
@@ -1,5 +1,6 @@
package app.seb3thehacker.gearslip.ui package app.seb3thehacker.gearslip.ui
import androidx.compose.runtime.withFrameNanos
import android.net.Uri import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@@ -83,6 +84,16 @@ fun SettingsScreen(
atUsageNotes: Boolean = false, atUsageNotes: Boolean = false,
) { ) {
val scroll = rememberScrollState() val scroll = rememberScrollState()
// Built a section per frame after the first two, so the screen shows at once instead of
// freezing while all of it composes; once built, it scrolls without composing anything new.
var built by remember { mutableIntStateOf(2) }
LaunchedEffect(Unit) {
while (built < SECTIONS) {
withFrameNanos { }
built++
}
}
fun ready(n: Int) = built > n
// Opened from Home's "it worked" card: start at usage notes. // Opened from Home's "it worked" card: start at usage notes.
var usageNotesY by remember { mutableIntStateOf(-1) } var usageNotesY by remember { mutableIntStateOf(-1) }
if (atUsageNotes) { if (atUsageNotes) {
@@ -128,25 +139,28 @@ fun SettingsScreen(
"Support Gearslip", "Support Gearslip",
footer = "Gearslip is free, with no ads and no tracking. Your support keeps it going.", footer = "Gearslip is free, with no ads and no tracking. Your support keeps it going.",
) { DonateRows() } ) { DonateRows() }
ConnectionSection() if (ready(2)) ConnectionSection()
CarAudioSection() if (ready(3)) CarAudioSection()
CarScreenSection() if (ready(4)) CarScreenSection()
VoiceModelSection() if (ready(5)) VoiceModelSection()
if (SHOW_RECOMMENDED_APPS) AppsSection(onOpenRecommendedApps) if (SHOW_RECOMMENDED_APPS && ready(6)) AppsSection(onOpenRecommendedApps)
SettingsSection( if (ready(7)) SettingsSection(
"Usage notes", "Usage notes",
Modifier.onGloballyPositioned { usageNotesY = it.positionInParent().y.toInt() }, Modifier.onGloballyPositioned { usageNotesY = it.positionInParent().y.toInt() },
header = USAGE_STATS_INTRO, header = USAGE_STATS_INTRO,
) { UsageStatsRows() } ) { UsageStatsRows() }
SettingsSection("Updates") { UpdateRow() } if (ready(8)) SettingsSection("Updates") { UpdateRow() }
CertificateSection() if (ready(9)) CertificateSection()
AboutSection(onReplayTutorial) if (ready(10)) AboutSection(onReplayTutorial)
if (BuildConfig.DEBUG) DebugSection() if (BuildConfig.DEBUG && ready(11)) DebugSection()
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
} }
} }
} }
/** How many sections Settings builds one frame at a time; see [ready]. */
private const val SECTIONS = 12
@Composable @Composable
private fun Arrow() = Icon( private fun Arrow() = Icon(
Icons.AutoMirrored.Filled.KeyboardArrowRight, Icons.AutoMirrored.Filled.KeyboardArrowRight,