@@ -7,6 +7,8 @@ import android.text.format.Formatter
77import androidx.activity.ComponentActivity
88import androidx.activity.compose.setContent
99import androidx.activity.enableEdgeToEdge
10+ import androidx.annotation.DrawableRes
11+ import androidx.annotation.StringRes
1012import androidx.compose.animation.Crossfade
1113import androidx.compose.animation.core.FastOutSlowInEasing
1214import androidx.compose.animation.core.LinearEasing
@@ -70,6 +72,8 @@ import androidx.compose.ui.platform.LocalContext
7072import androidx.compose.ui.platform.LocalResources
7173import androidx.compose.ui.res.painterResource
7274import androidx.compose.ui.res.stringResource
75+ import androidx.compose.ui.text.TextStyle
76+ import androidx.compose.ui.text.font.FontFamily
7377import androidx.compose.ui.text.style.TextAlign
7478import androidx.compose.ui.tooling.preview.Preview
7579import androidx.compose.ui.unit.dp
@@ -82,12 +86,14 @@ import com.geode.launcher.ui.theme.Theme
8286import com.geode.launcher.ui.theme.Theme.DARK
8387import com.geode.launcher.ui.theme.Theme.LIGHT
8488import com.geode.launcher.ui.theme.launcherTitleStyle
89+ import com.geode.launcher.ui.theme.sapphireTitleStyle
8590import com.geode.launcher.updater.ReleaseManager
8691import com.geode.launcher.utils.ApplicationIcon
8792import com.geode.launcher.utils.Constants
8893import com.geode.launcher.utils.GamePackageUtils
8994import com.geode.launcher.utils.LaunchUtils
9095import com.geode.launcher.utils.PreferenceUtils
96+ import com.geode.launcher.utils.checkIconDate
9197import kotlinx.coroutines.CancellationException
9298import kotlinx.coroutines.launch
9399import java.net.ConnectException
@@ -453,35 +459,104 @@ fun ExtraOptions(onSettings: () -> Unit) {
453459 }
454460}
455461
456- val lightOuterColorGradient = arrayOf(
457- 0.0f to Color (0xfff5b11d ),
458- 0.10f to Color (0xfff0834d ),
459- 0.24f to Color (0xffe85f6b ),
460- 0.49f to Color (0xffc9659e ),
461- 1.0f to Color (0xffb9588f )
462+ data class ColorPalette (
463+ val outerColors : List <Pair <Float , Color >>,
464+ val innerColors : List <Pair <Float , Color >>,
462465)
463466
464- val lightInnerColorGradient = arrayOf(
465- 0.48f to Color (0xfff2ce00 ),
466- 0.63f to Color (0xfff4b97e ),
467- 0.73f to Color (0xfff7b66a ),
468- 1.0f to Color (0xffeb8fac )
467+ data class BrandPalette (
468+ val lightPalette : ColorPalette ,
469+ val darkPalette : ColorPalette ,
470+
471+ @param:DrawableRes val innerId : Int ,
472+ @param:DrawableRes val outerId : Int ,
473+
474+ @param:DrawableRes val darkLogo : Int ,
475+ @param:DrawableRes val lightLogo : Int ,
476+
477+ @param:StringRes val title : Int ,
478+ val titleFont : TextStyle ,
469479)
470480
471- val darkOuterColorGradient = arrayOf(
472- 0.0f to Color (0xfffffdff ),
473- 0.08f to Color (0xffF2EAF5 ),
474- 0.12f to Color (0xffEDE5EF ),
475- 0.31f to Color (0xffCDB5CD ),
476- 0.49f to Color (0xffBA9BBC ),
477- 1.0f to Color (0xff8D7ACF ),
481+ val geodeColorPalette = BrandPalette (
482+ lightPalette = ColorPalette (
483+ outerColors = listOf (
484+ 0.0f to Color (0xfff5b11d ),
485+ 0.10f to Color (0xfff0834d ),
486+ 0.24f to Color (0xffe85f6b ),
487+ 0.49f to Color (0xffc9659e ),
488+ 1.0f to Color (0xffb9588f )
489+ ),
490+ innerColors = listOf (
491+ 0.48f to Color (0xfff2ce00 ),
492+ 0.63f to Color (0xfff4b97e ),
493+ 0.73f to Color (0xfff7b66a ),
494+ 1.0f to Color (0xffeb8fac )
495+ )
496+ ),
497+ darkPalette = ColorPalette (
498+ outerColors = listOf (
499+ 0.0f to Color (0xfffffdff ),
500+ 0.08f to Color (0xffF2EAF5 ),
501+ 0.12f to Color (0xffEDE5EF ),
502+ 0.31f to Color (0xffCDB5CD ),
503+ 0.49f to Color (0xffBA9BBC ),
504+ 1.0f to Color (0xff8D7ACF ),
505+ ),
506+ innerColors = listOf (
507+ 0.48f to Color (0xffffffff ),
508+ 0.63f to Color (0xfff5e4c2 ),
509+ 0.73f to Color (0xffe5c7ad ),
510+ 1.0f to Color (0xffb790a9 ),
511+ )
512+ ),
513+ innerId = R .drawable.geode_monochrome_inner,
514+ outerId = R .drawable.geode_monochrome_outer,
515+ darkLogo = R .drawable.geode_base,
516+ lightLogo = R .drawable.geode_base_light,
517+ title = R .string.launcher_title,
518+ titleFont = launcherTitleStyle
478519)
479520
480- val darkInnerColorGradient = arrayOf(
481- 0.48f to Color (0xffffffff ),
482- 0.63f to Color (0xfff5e4c2 ),
483- 0.73f to Color (0xffe5c7ad ),
484- 1.0f to Color (0xffb790a9 ),
521+ val sapphireColorPalette = BrandPalette (
522+ lightPalette = ColorPalette (
523+ outerColors = listOf (
524+ 0.0f to Color (0xff906FFF ),
525+ 0.08f to Color (0xff9E81FF ),
526+ 0.12f to Color (0xffAB8DFF ),
527+ 0.31f to Color (0xffBE89FF ),
528+ 0.49f to Color (0xff979BD7 ),
529+ 1.0f to Color (0xff558DDA ),
530+ ),
531+ innerColors = listOf (
532+ 0.48f to Color (0xffFFB65C ),
533+ 0.63f to Color (0xffFFADA4 ),
534+ 0.73f to Color (0xffF0A3A4 ),
535+ 1.0f to Color (0xffD066A6 ),
536+ )
537+ ),
538+ darkPalette = ColorPalette (
539+ outerColors = listOf (
540+ 0.0f to Color (0xfffffdff ),
541+ 0.08f to Color (0xffF2EAF5 ),
542+ 0.12f to Color (0xffEDE5EF ),
543+ 0.31f to Color (0xffC0B5CD ),
544+ 0.49f to Color (0xff9B9DBC ),
545+ 1.0f to Color (0xff558DDA ),
546+ ),
547+ innerColors = listOf (
548+ 0.48f to Color (0xffFFF9F5 ),
549+ 0.63f to Color (0xffF2DCD3 ),
550+ 0.73f to Color (0xffDBA7A8 ),
551+ 1.0f to Color (0xffD066A6 ),
552+ )
553+ ),
554+ innerId = R .drawable.sapphire_monochrome_inner,
555+ outerId = R .drawable.sapphire_monochrome_outer,
556+ darkLogo = R .drawable.sapphire_base,
557+ lightLogo = R .drawable.sapphire_base_light,
558+ title = R .string.application_icon_sapphire,
559+ titleFont = sapphireTitleStyle
485560)
486561
487562@Preview(name = " non-animated, light" )
@@ -512,18 +587,43 @@ fun GeodeLogoAnimatedPreview() {
512587 }
513588}
514589
590+ @Preview(name = " sapphire, non-animated, light" )
591+ @Preview(name = " sapphire, non-animated, dark" , uiMode = UI_MODE_NIGHT_YES )
515592@Composable
516- fun AnimatedLogo (modifier : Modifier = Modifier , sapphire : Boolean = false) {
517- val theme = LocalTheme .current
518- val innerColorPalette = remember(theme) {
519- if (theme == LIGHT ) lightInnerColorGradient
520- else darkInnerColorGradient
593+ fun SapphireLogoPreview () {
594+ val theme = if (isSystemInDarkTheme()) DARK else LIGHT
595+ CompositionLocalProvider (LocalTheme provides theme) {
596+ GeodeLauncherTheme (theme = theme) {
597+ Surface {
598+ GeodeLogo (shouldAnimate = false , basePalette = sapphireColorPalette)
599+ }
600+ }
521601 }
602+ }
522603
523- val outerColorPalette = remember(theme) {
524- if (theme == LIGHT ) lightOuterColorGradient
525- else darkOuterColorGradient
604+ @Preview(name = " sapphire, animated, light" )
605+ @Preview(name = " sapphire, animated, dark" , uiMode = UI_MODE_NIGHT_YES )
606+ @Composable
607+ fun SapphireLogoAnimatedPreview () {
608+ val theme = if (isSystemInDarkTheme()) DARK else LIGHT
609+ CompositionLocalProvider (LocalTheme provides theme) {
610+ GeodeLauncherTheme (theme = theme) {
611+ Surface {
612+ GeodeLogo (shouldAnimate = true , basePalette = sapphireColorPalette)
613+ }
614+ }
526615 }
616+ }
617+
618+ @Composable
619+ fun AnimatedLogo (modifier : Modifier = Modifier , basePalette : BrandPalette ) {
620+ val theme = LocalTheme .current
621+
622+ val colorPalette = if (theme == LIGHT ) basePalette.lightPalette
623+ else basePalette.darkPalette
624+
625+ val innerColorPalette = colorPalette.innerColors
626+ val outerColorPalette = colorPalette.outerColors
527627
528628 val infiniteTransition = rememberInfiniteTransition(label = " infinite transition" )
529629 val offset by infiniteTransition.animateFloat(
@@ -537,23 +637,22 @@ fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) {
537637 )
538638
539639 val innerBrush = Brush .linearGradient(
540- colorStops = innerColorPalette,
640+ colorStops = innerColorPalette.toTypedArray() ,
541641 start = Offset (0.0f , 0.0f + (165.06f * offset)),
542642 end = Offset (0.0f , 165.06f + (165.06f * offset)),
543643 tileMode = TileMode .Mirror
544644 )
545645
546646 val outerBrush = Brush .linearGradient(
547- colorStops = outerColorPalette,
647+ colorStops = outerColorPalette.toTypedArray() ,
548648 start = Offset (0.0f + (84.69f * offset), 0.0f + (165.06f * offset)),
549649 end = Offset (84.69f + (84.69f * offset), 169.61f + (165.06f * offset)),
550650 tileMode = TileMode .Mirror
551651 )
552652
553653 Box (modifier = modifier) {
554654 Icon (
555- painter = if (sapphire) painterResource(R .drawable.sapphire_monochrome_inner)
556- else painterResource(id = R .drawable.geode_monochrome_inner),
655+ painter = painterResource(basePalette.innerId),
557656 contentDescription = null ,
558657 modifier = Modifier
559658 .graphicsLayer(compositingStrategy = CompositingStrategy .Offscreen )
@@ -566,8 +665,7 @@ fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) {
566665 }
567666 )
568667 Icon (
569- painter = if (sapphire) painterResource(R .drawable.sapphire_monochrome_outer)
570- else painterResource(id = R .drawable.geode_monochrome_outer),
668+ painter = painterResource(basePalette.outerId),
571669 contentDescription = null ,
572670 modifier = Modifier
573671 .graphicsLayer(compositingStrategy = CompositingStrategy .Offscreen )
@@ -583,49 +681,34 @@ fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) {
583681}
584682
585683@Composable
586- fun GeodeLogo (modifier : Modifier = Modifier , shouldAnimate : Boolean = false) {
684+ fun GeodeLogo (modifier : Modifier = Modifier , shouldAnimate : Boolean = false, basePalette : BrandPalette = geodeColorPalette ) {
587685 Row (
588686 horizontalArrangement = Arrangement .Center ,
589687 verticalAlignment = Alignment .CenterVertically ,
590688 modifier = modifier
591689 ) {
592690 val theme = LocalTheme .current
593691
594- val context = LocalContext .current
595- val sapphireLogo = remember {
596- PreferenceUtils .get(context).getString(PreferenceUtils .Key .SELECTED_ICON ) == ApplicationIcon .SAPPHIRE .toId()
597- }
598-
599- if (sapphireLogo) {
600- Icon (
601- painterResource(R .drawable.sapphire_monochrome),
602- contentDescription = null ,
603- modifier = Modifier .size(64 .dp, 64 .dp)
604- )
605- } else {
606- Crossfade (
607- targetState = shouldAnimate,
608- animationSpec = tween(durationMillis = 1000 , easing = FastOutSlowInEasing ),
609- label= " logo fade"
610- ) { screen ->
611- when (screen) {
612- true -> AnimatedLogo (modifier = Modifier .size(64 .dp, 64 .dp))
613- false -> Image (
614- painterResource(if (theme == LIGHT )
615- R .drawable.geode_base_light else R .drawable.geode_base
616- ),
617- contentDescription = null ,
618- modifier = Modifier .size(64 .dp, 64 .dp)
619- )
620- }
692+ Crossfade (
693+ targetState = shouldAnimate,
694+ animationSpec = tween(durationMillis = 1000 , easing = FastOutSlowInEasing ),
695+ label= " logo fade"
696+ ) { screen ->
697+ when (screen) {
698+ true -> AnimatedLogo (modifier = Modifier .size(64 .dp, 64 .dp), basePalette = basePalette)
699+ false -> Image (
700+ painterResource(if (theme == LIGHT )
701+ basePalette.lightLogo else basePalette.darkLogo
702+ ),
703+ contentDescription = null ,
704+ modifier = Modifier .size(64 .dp, 64 .dp)
705+ )
621706 }
622-
623707 }
624708
625709 Text (
626- if (sapphireLogo) stringResource(R .string.application_icon_sapphire)
627- else stringResource(R .string.launcher_title),
628- style = launcherTitleStyle,
710+ stringResource(basePalette.title),
711+ style = basePalette.titleFont,
629712 fontSize = 64 .sp,
630713 modifier = Modifier
631714 .padding(12 .dp)
@@ -713,7 +796,14 @@ fun AltMainScreen(
713796 .padding(8 .dp)
714797 .verticalScroll(rememberScrollState())
715798 ) {
716- GeodeLogo (shouldAnimate = launchUIState.isInProgress())
799+ val sapphireLogo = remember {
800+ checkIconDate() || PreferenceUtils .get(context)
801+ .getString(PreferenceUtils .Key .SELECTED_ICON ) == ApplicationIcon .SAPPHIRE .toId()
802+ }
803+
804+ val basePalette = if (sapphireLogo) sapphireColorPalette
805+ else geodeColorPalette
806+ GeodeLogo (shouldAnimate = launchUIState.isInProgress(), basePalette = basePalette)
717807
718808 Column (
719809 verticalArrangement = Arrangement .spacedBy(8 .dp),
0 commit comments