diff --git a/app/src/main/java/com/geode/launcher/MainActivity.kt b/app/src/main/java/com/geode/launcher/MainActivity.kt index f85d4044..fa3dd282 100644 --- a/app/src/main/java/com/geode/launcher/MainActivity.kt +++ b/app/src/main/java/com/geode/launcher/MainActivity.kt @@ -7,6 +7,8 @@ import android.text.format.Formatter import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.compose.animation.Crossfade import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.LinearEasing @@ -70,6 +72,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -82,12 +86,14 @@ import com.geode.launcher.ui.theme.Theme import com.geode.launcher.ui.theme.Theme.DARK import com.geode.launcher.ui.theme.Theme.LIGHT import com.geode.launcher.ui.theme.launcherTitleStyle +import com.geode.launcher.ui.theme.sapphireTitleStyle import com.geode.launcher.updater.ReleaseManager import com.geode.launcher.utils.ApplicationIcon import com.geode.launcher.utils.Constants import com.geode.launcher.utils.GamePackageUtils import com.geode.launcher.utils.LaunchUtils import com.geode.launcher.utils.PreferenceUtils +import com.geode.launcher.utils.checkIconDate import kotlinx.coroutines.CancellationException import kotlinx.coroutines.launch import java.net.ConnectException @@ -453,35 +459,104 @@ fun ExtraOptions(onSettings: () -> Unit) { } } -val lightOuterColorGradient = arrayOf( - 0.0f to Color(0xfff5b11d), - 0.10f to Color(0xfff0834d), - 0.24f to Color(0xffe85f6b), - 0.49f to Color(0xffc9659e), - 1.0f to Color(0xffb9588f) +data class ColorPalette( + val outerColors: List>, + val innerColors: List>, ) -val lightInnerColorGradient = arrayOf( - 0.48f to Color(0xfff2ce00), - 0.63f to Color(0xfff4b97e), - 0.73f to Color(0xfff7b66a), - 1.0f to Color(0xffeb8fac) +data class BrandPalette( + val lightPalette: ColorPalette, + val darkPalette: ColorPalette, + + @param:DrawableRes val innerId: Int, + @param:DrawableRes val outerId: Int, + + @param:DrawableRes val darkLogo: Int, + @param:DrawableRes val lightLogo: Int, + + @param:StringRes val title: Int, + val titleFont: TextStyle, ) -val darkOuterColorGradient = arrayOf( - 0.0f to Color(0xfffffdff), - 0.08f to Color(0xffF2EAF5), - 0.12f to Color(0xffEDE5EF), - 0.31f to Color(0xffCDB5CD), - 0.49f to Color(0xffBA9BBC), - 1.0f to Color(0xff8D7ACF), +val geodeColorPalette = BrandPalette( + lightPalette = ColorPalette( + outerColors = listOf( + 0.0f to Color(0xfff5b11d), + 0.10f to Color(0xfff0834d), + 0.24f to Color(0xffe85f6b), + 0.49f to Color(0xffc9659e), + 1.0f to Color(0xffb9588f) + ), + innerColors = listOf( + 0.48f to Color(0xfff2ce00), + 0.63f to Color(0xfff4b97e), + 0.73f to Color(0xfff7b66a), + 1.0f to Color(0xffeb8fac) + ) + ), + darkPalette = ColorPalette( + outerColors = listOf( + 0.0f to Color(0xfffffdff), + 0.08f to Color(0xffF2EAF5), + 0.12f to Color(0xffEDE5EF), + 0.31f to Color(0xffCDB5CD), + 0.49f to Color(0xffBA9BBC), + 1.0f to Color(0xff8D7ACF), + ), + innerColors = listOf( + 0.48f to Color(0xffffffff), + 0.63f to Color(0xfff5e4c2), + 0.73f to Color(0xffe5c7ad), + 1.0f to Color(0xffb790a9), + ) + ), + innerId = R.drawable.geode_monochrome_inner, + outerId = R.drawable.geode_monochrome_outer, + darkLogo = R.drawable.geode_base, + lightLogo = R.drawable.geode_base_light, + title = R.string.launcher_title, + titleFont = launcherTitleStyle ) -val darkInnerColorGradient = arrayOf( - 0.48f to Color(0xffffffff), - 0.63f to Color(0xfff5e4c2), - 0.73f to Color(0xffe5c7ad), - 1.0f to Color(0xffb790a9), +val sapphireColorPalette = BrandPalette( + lightPalette = ColorPalette( + outerColors = listOf( + 0.0f to Color(0xff906FFF), + 0.08f to Color(0xff9E81FF), + 0.12f to Color(0xffAB8DFF), + 0.31f to Color(0xffBE89FF), + 0.49f to Color(0xff979BD7), + 1.0f to Color(0xff558DDA), + ), + innerColors = listOf( + 0.48f to Color(0xffFFB65C), + 0.63f to Color(0xffFFADA4), + 0.73f to Color(0xffF0A3A4), + 1.0f to Color(0xffD066A6), + ) + ), + darkPalette = ColorPalette( + outerColors = listOf( + 0.0f to Color(0xfffffdff), + 0.08f to Color(0xffF2EAF5), + 0.12f to Color(0xffEDE5EF), + 0.31f to Color(0xffC0B5CD), + 0.49f to Color(0xff9B9DBC), + 1.0f to Color(0xff558DDA), + ), + innerColors = listOf( + 0.48f to Color(0xffFFF9F5), + 0.63f to Color(0xffF2DCD3), + 0.73f to Color(0xffDBA7A8), + 1.0f to Color(0xffD066A6), + ) + ), + innerId = R.drawable.sapphire_monochrome_inner, + outerId = R.drawable.sapphire_monochrome_outer, + darkLogo = R.drawable.sapphire_base, + lightLogo = R.drawable.sapphire_base_light, + title = R.string.application_icon_sapphire, + titleFont = sapphireTitleStyle ) @Preview(name = "non-animated, light") @@ -512,18 +587,43 @@ fun GeodeLogoAnimatedPreview() { } } +@Preview(name = "sapphire, non-animated, light") +@Preview(name = "sapphire, non-animated, dark", uiMode = UI_MODE_NIGHT_YES) @Composable -fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) { - val theme = LocalTheme.current - val innerColorPalette = remember(theme) { - if (theme == LIGHT) lightInnerColorGradient - else darkInnerColorGradient +fun SapphireLogoPreview() { + val theme = if (isSystemInDarkTheme()) DARK else LIGHT + CompositionLocalProvider(LocalTheme provides theme) { + GeodeLauncherTheme(theme = theme) { + Surface { + GeodeLogo(shouldAnimate = false, basePalette = sapphireColorPalette) + } + } } +} - val outerColorPalette = remember(theme) { - if (theme == LIGHT) lightOuterColorGradient - else darkOuterColorGradient +@Preview(name = "sapphire, animated, light") +@Preview(name = "sapphire, animated, dark", uiMode = UI_MODE_NIGHT_YES) +@Composable +fun SapphireLogoAnimatedPreview() { + val theme = if (isSystemInDarkTheme()) DARK else LIGHT + CompositionLocalProvider(LocalTheme provides theme) { + GeodeLauncherTheme(theme = theme) { + Surface { + GeodeLogo(shouldAnimate = true, basePalette = sapphireColorPalette) + } + } } +} + +@Composable +fun AnimatedLogo(modifier: Modifier = Modifier, basePalette: BrandPalette) { + val theme = LocalTheme.current + + val colorPalette = if (theme == LIGHT) basePalette.lightPalette + else basePalette.darkPalette + + val innerColorPalette = colorPalette.innerColors + val outerColorPalette = colorPalette.outerColors val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val offset by infiniteTransition.animateFloat( @@ -537,14 +637,14 @@ fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) { ) val innerBrush = Brush.linearGradient( - colorStops = innerColorPalette, + colorStops = innerColorPalette.toTypedArray(), start = Offset(0.0f, 0.0f + (165.06f * offset)), end = Offset(0.0f, 165.06f + (165.06f * offset)), tileMode = TileMode.Mirror ) val outerBrush = Brush.linearGradient( - colorStops = outerColorPalette, + colorStops = outerColorPalette.toTypedArray(), start = Offset(0.0f + (84.69f * offset), 0.0f + (165.06f * offset)), end = Offset(84.69f + (84.69f * offset), 169.61f + (165.06f * offset)), tileMode = TileMode.Mirror @@ -552,8 +652,7 @@ fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) { Box(modifier = modifier) { Icon( - painter = if (sapphire) painterResource(R.drawable.sapphire_monochrome_inner) - else painterResource(id = R.drawable.geode_monochrome_inner), + painter = painterResource(basePalette.innerId), contentDescription = null, modifier = Modifier .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen) @@ -566,8 +665,7 @@ fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) { } ) Icon( - painter = if (sapphire) painterResource(R.drawable.sapphire_monochrome_outer) - else painterResource(id = R.drawable.geode_monochrome_outer), + painter = painterResource(basePalette.outerId), contentDescription = null, modifier = Modifier .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen) @@ -583,7 +681,7 @@ fun AnimatedLogo(modifier: Modifier = Modifier, sapphire: Boolean = false) { } @Composable -fun GeodeLogo(modifier: Modifier = Modifier, shouldAnimate: Boolean = false) { +fun GeodeLogo(modifier: Modifier = Modifier, shouldAnimate: Boolean = false, basePalette: BrandPalette = geodeColorPalette) { Row( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, @@ -591,41 +689,26 @@ fun GeodeLogo(modifier: Modifier = Modifier, shouldAnimate: Boolean = false) { ) { val theme = LocalTheme.current - val context = LocalContext.current - val sapphireLogo = remember { - PreferenceUtils.get(context).getString(PreferenceUtils.Key.SELECTED_ICON) == ApplicationIcon.SAPPHIRE.toId() - } - - if (sapphireLogo) { - Icon( - painterResource(R.drawable.sapphire_monochrome), - contentDescription = null, - modifier = Modifier.size(64.dp, 64.dp) - ) - } else { - Crossfade( - targetState = shouldAnimate, - animationSpec = tween(durationMillis = 1000, easing = FastOutSlowInEasing), - label="logo fade" - ) { screen -> - when (screen) { - true -> AnimatedLogo(modifier = Modifier.size(64.dp, 64.dp)) - false -> Image( - painterResource(if (theme == LIGHT) - R.drawable.geode_base_light else R.drawable.geode_base - ), - contentDescription = null, - modifier = Modifier.size(64.dp, 64.dp) - ) - } + Crossfade( + targetState = shouldAnimate, + animationSpec = tween(durationMillis = 1000, easing = FastOutSlowInEasing), + label="logo fade" + ) { screen -> + when (screen) { + true -> AnimatedLogo(modifier = Modifier.size(64.dp, 64.dp), basePalette = basePalette) + false -> Image( + painterResource(if (theme == LIGHT) + basePalette.lightLogo else basePalette.darkLogo + ), + contentDescription = null, + modifier = Modifier.size(64.dp, 64.dp) + ) } - } Text( - if (sapphireLogo) stringResource(R.string.application_icon_sapphire) - else stringResource(R.string.launcher_title), - style = launcherTitleStyle, + stringResource(basePalette.title), + style = basePalette.titleFont, fontSize = 64.sp, modifier = Modifier .padding(12.dp) @@ -713,7 +796,14 @@ fun AltMainScreen( .padding(8.dp) .verticalScroll(rememberScrollState()) ) { - GeodeLogo(shouldAnimate = launchUIState.isInProgress()) + val sapphireLogo = remember { + checkIconDate() || PreferenceUtils.get(context) + .getString(PreferenceUtils.Key.SELECTED_ICON) == ApplicationIcon.SAPPHIRE.toId() + } + + val basePalette = if (sapphireLogo) sapphireColorPalette + else geodeColorPalette + GeodeLogo(shouldAnimate = launchUIState.isInProgress(), basePalette = basePalette) Column( verticalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/src/main/java/com/geode/launcher/preferences/ApplicationIconActivity.kt b/app/src/main/java/com/geode/launcher/preferences/ApplicationIconActivity.kt index c3a0cc93..cfc529a0 100644 --- a/app/src/main/java/com/geode/launcher/preferences/ApplicationIconActivity.kt +++ b/app/src/main/java/com/geode/launcher/preferences/ApplicationIconActivity.kt @@ -60,10 +60,7 @@ import com.geode.launcher.utils.ApplicationIcon import com.geode.launcher.utils.ApplicationIconDetails import com.geode.launcher.utils.IconUtils import com.geode.launcher.utils.adaptiveIconPainterResource -import kotlinx.datetime.Month -import kotlinx.datetime.TimeZone -import kotlinx.datetime.todayIn -import kotlin.time.Clock +import com.geode.launcher.utils.checkIconDate class ApplicationIconActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -235,10 +232,7 @@ fun ApplicationIconScreen( ) { val selectedIcon by PreferenceUtils.useStringPreference(PreferenceUtils.Key.SELECTED_ICON) - val aprilFools = remember { - val currentDate = Clock.System.todayIn(TimeZone.currentSystemDefault()) - currentDate.month == Month.APRIL && currentDate.day <= 7 - } + val aprilFools = remember { checkIconDate() } Column(Modifier .padding(horizontal = 12.dp) diff --git a/app/src/main/java/com/geode/launcher/ui/theme/Font.kt b/app/src/main/java/com/geode/launcher/ui/theme/Font.kt index 3ecac9de..5978b929 100644 --- a/app/src/main/java/com/geode/launcher/ui/theme/Font.kt +++ b/app/src/main/java/com/geode/launcher/ui/theme/Font.kt @@ -16,9 +16,20 @@ val quicksandFamily = FontFamily( Font(R.font.quicksand_semibold, FontWeight.SemiBold) ) +val googleSansFamily = FontFamily( + Font(R.font.googlesans_medium, FontWeight.Medium) +) + val launcherTitleStyle = TextStyle( fontFamily = quicksandFamily, fontWeight = FontWeight.SemiBold, fontSize = 32.sp, letterSpacing = 0.sp +) + +val sapphireTitleStyle = TextStyle( + fontFamily = googleSansFamily, + fontWeight = FontWeight.Medium, + fontSize = 32.sp, + letterSpacing = 0.sp ) \ No newline at end of file diff --git a/app/src/main/java/com/geode/launcher/utils/IconUtils.kt b/app/src/main/java/com/geode/launcher/utils/IconUtils.kt index 2a4ee270..c2ffbafc 100644 --- a/app/src/main/java/com/geode/launcher/utils/IconUtils.kt +++ b/app/src/main/java/com/geode/launcher/utils/IconUtils.kt @@ -15,6 +15,10 @@ import androidx.compose.ui.res.painterResource import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.toBitmap import com.geode.launcher.R +import kotlinx.datetime.Month +import kotlinx.datetime.TimeZone +import kotlinx.datetime.todayIn +import kotlin.time.Clock // https://gist.github.com/tkuenneth/ddf598663f041dc79960cda503d14448?permalink_comment_id=4660486#gistcomment-4660486 @Composable @@ -66,6 +70,11 @@ enum class ApplicationIcon { } } +fun checkIconDate(): Boolean { + val currentDate = Clock.System.todayIn(TimeZone.currentSystemDefault()) + return currentDate.month == Month.APRIL && currentDate.day <= 7 +} + data class ApplicationIconDetails( val id: String, val component: String, diff --git a/app/src/main/res/drawable/sapphire_base.xml b/app/src/main/res/drawable/sapphire_base.xml new file mode 100644 index 00000000..43e4d683 --- /dev/null +++ b/app/src/main/res/drawable/sapphire_base.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/sapphire_base_light.xml b/app/src/main/res/drawable/sapphire_base_light.xml new file mode 100644 index 00000000..53eee38a --- /dev/null +++ b/app/src/main/res/drawable/sapphire_base_light.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/font/googlesans_medium.ttf b/app/src/main/res/font/googlesans_medium.ttf new file mode 100644 index 00000000..8b9aebc9 Binary files /dev/null and b/app/src/main/res/font/googlesans_medium.ttf differ