Bruno Charest 98f2ef2e7e feat: Migrate UI theme system from hardcoded colors to Material3 dynamic theming
- Inject ThemePreferences into MainActivity and apply dynamic theme selection
- Replace all hardcoded color references (DeepNavy, DarkNavy, CyanPrimary, TextPrimary, etc.) with MaterialTheme.colorScheme equivalents throughout AddLinkScreen and LoginScreen
- Update component colors to use Material3 color roles (primary, onBackground, surfaceVariant, outline, etc.)
- Migrate GlassCard, buttons, text fields, switches, and progress
2026-02-09 12:56:33 -05:00

492 lines
20 KiB
Kotlin

package com.shaarit.ui.theme
import android.app.Activity
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
// Premium Dark Theme Colors - Inspired by modern SaaS interfaces
val DeepNavy = Color(0xFF0A1628)
val DarkNavy = Color(0xFF0D1B2A)
val CardBackground = Color(0xFF1B2838)
val CardBackgroundElevated = Color(0xFF243447)
val SurfaceVariant = Color(0xFF2A3F54)
// Accent Colors
val CyanPrimary = Color(0xFF00D4AA)
val CyanLight = Color(0xFF4EECC4)
val TealSecondary = Color(0xFF0EA5E9)
val TealLight = Color(0xFF38BDF8)
// Text Colors
val TextPrimary = Color(0xFFE2E8F0)
val TextSecondary = Color(0xFF94A3B8)
val TextMuted = Color(0xFF64748B)
// Status Colors
val SuccessGreen = Color(0xFF10B981)
val WarningAmber = Color(0xFFF59E0B)
val ErrorRed = Color(0xFFEF4444)
// Gradient Colors (for reference in custom components)
val GradientStart = Color(0xFF0EA5E9)
val GradientEnd = Color(0xFF00D4AA)
// AI/Magic Colors
val Purple = Color(0xFFA855F7)
val PurpleLight = Color(0xFFC084FC)
// ── Default Theme (ShaarIt) ──
private val DefaultDarkColorScheme =
darkColorScheme(
primary = CyanPrimary,
onPrimary = DeepNavy,
primaryContainer = CardBackgroundElevated,
onPrimaryContainer = CyanLight,
secondary = TealSecondary,
onSecondary = DeepNavy,
secondaryContainer = SurfaceVariant,
onSecondaryContainer = TealLight,
tertiary = CyanLight,
onTertiary = DeepNavy,
background = DeepNavy,
onBackground = TextPrimary,
surface = DarkNavy,
onSurface = TextPrimary,
surfaceVariant = CardBackground,
onSurfaceVariant = TextSecondary,
outline = TextMuted,
outlineVariant = SurfaceVariant,
error = ErrorRed,
onError = Color.White,
errorContainer = Color(0xFF450A0A),
onErrorContainer = Color(0xFFFCA5A5)
)
// ── GitHub (Smooth Dark) ──
private val GitHubDarkColorScheme =
darkColorScheme(
primary = Color(0xFF58A6FF),
onPrimary = Color(0xFF0D1117),
primaryContainer = Color(0xFF1F2937),
onPrimaryContainer = Color(0xFF79C0FF),
secondary = Color(0xFF3FB950),
onSecondary = Color(0xFF0D1117),
secondaryContainer = Color(0xFF1C2D22),
onSecondaryContainer = Color(0xFF56D364),
tertiary = Color(0xFFD2A8FF),
onTertiary = Color(0xFF0D1117),
background = Color(0xFF0D1117),
onBackground = Color(0xFFC9D1D9),
surface = Color(0xFF161B22),
onSurface = Color(0xFFC9D1D9),
surfaceVariant = Color(0xFF21262D),
onSurfaceVariant = Color(0xFF8B949E),
outline = Color(0xFF30363D),
outlineVariant = Color(0xFF21262D),
error = Color(0xFFF85149),
onError = Color.White,
errorContainer = Color(0xFF490202),
onErrorContainer = Color(0xFFFFA198)
)
// ── Linear (Soft Dark) ──
private val LinearDarkColorScheme =
darkColorScheme(
primary = Color(0xFF5E6AD2),
onPrimary = Color.White,
primaryContainer = Color(0xFF2A2B3D),
onPrimaryContainer = Color(0xFF8B8FE8),
secondary = Color(0xFF4EA7FC),
onSecondary = Color(0xFF12131A),
secondaryContainer = Color(0xFF1E2A3A),
onSecondaryContainer = Color(0xFF7DC4FF),
tertiary = Color(0xFFE8A861),
onTertiary = Color(0xFF12131A),
background = Color(0xFF12131A),
onBackground = Color(0xFFEEEFF2),
surface = Color(0xFF1B1C24),
onSurface = Color(0xFFEEEFF2),
surfaceVariant = Color(0xFF22232E),
onSurfaceVariant = Color(0xFF8A8F98),
outline = Color(0xFF3B3D4A),
outlineVariant = Color(0xFF2A2B3D),
error = Color(0xFFEB5757),
onError = Color.White,
errorContainer = Color(0xFF3D1515),
onErrorContainer = Color(0xFFFF9B9B)
)
// ── Spotify (Jet Black) ──
private val SpotifyDarkColorScheme =
darkColorScheme(
primary = Color(0xFF1DB954),
onPrimary = Color.Black,
primaryContainer = Color(0xFF1A3D27),
onPrimaryContainer = Color(0xFF1ED760),
secondary = Color(0xFF1DB954),
onSecondary = Color.Black,
secondaryContainer = Color(0xFF1A3D27),
onSecondaryContainer = Color(0xFF1ED760),
tertiary = Color(0xFFB3B3B3),
onTertiary = Color.Black,
background = Color(0xFF000000),
onBackground = Color(0xFFFFFFFF),
surface = Color(0xFF121212),
onSurface = Color(0xFFFFFFFF),
surfaceVariant = Color(0xFF1A1A1A),
onSurfaceVariant = Color(0xFFB3B3B3),
outline = Color(0xFF333333),
outlineVariant = Color(0xFF282828),
error = Color(0xFFE22134),
onError = Color.White,
errorContainer = Color(0xFF3D0A0F),
onErrorContainer = Color(0xFFFF6B7A)
)
// ── Notion (Clean Dark) ──
private val NotionDarkColorScheme =
darkColorScheme(
primary = Color(0xFF529CCA),
onPrimary = Color(0xFF191919),
primaryContainer = Color(0xFF2A3A4A),
onPrimaryContainer = Color(0xFF7AB8E0),
secondary = Color(0xFFE07A5F),
onSecondary = Color(0xFF191919),
secondaryContainer = Color(0xFF3D2A22),
onSecondaryContainer = Color(0xFFF0A08A),
tertiary = Color(0xFF81B29A),
onTertiary = Color(0xFF191919),
background = Color(0xFF191919),
onBackground = Color(0xFFE0E0E0),
surface = Color(0xFF202020),
onSurface = Color(0xFFE0E0E0),
surfaceVariant = Color(0xFF2B2B2B),
onSurfaceVariant = Color(0xFF9B9B9B),
outline = Color(0xFF3E3E3E),
outlineVariant = Color(0xFF2F2F2F),
error = Color(0xFFEB5757),
onError = Color.White,
errorContainer = Color(0xFF3D1515),
onErrorContainer = Color(0xFFFF9B9B)
)
// ── Discord (Blurple Dark) ──
private val DiscordDarkColorScheme =
darkColorScheme(
primary = Color(0xFF5865F2),
onPrimary = Color.White,
primaryContainer = Color(0xFF3C45A5),
onPrimaryContainer = Color(0xFF9BA2FF),
secondary = Color(0xFF57F287),
onSecondary = Color(0xFF1E2124),
secondaryContainer = Color(0xFF234D35),
onSecondaryContainer = Color(0xFF7DFFA8),
tertiary = Color(0xFFFEE75C),
onTertiary = Color(0xFF1E2124),
background = Color(0xFF313338),
onBackground = Color(0xFFDBDEE1),
surface = Color(0xFF2B2D31),
onSurface = Color(0xFFDBDEE1),
surfaceVariant = Color(0xFF383A40),
onSurfaceVariant = Color(0xFFB5BAC1),
outline = Color(0xFF4E5058),
outlineVariant = Color(0xFF3F4147),
error = Color(0xFFED4245),
onError = Color.White,
errorContainer = Color(0xFF4D1516),
onErrorContainer = Color(0xFFFFA0A1)
)
// ── Dracula (Iconic Dark) ──
private val DraculaDarkColorScheme =
darkColorScheme(
primary = Color(0xFFBD93F9),
onPrimary = Color(0xFF21222C),
primaryContainer = Color(0xFF44475A),
onPrimaryContainer = Color(0xFFD6BCFA),
secondary = Color(0xFF50FA7B),
onSecondary = Color(0xFF21222C),
secondaryContainer = Color(0xFF2D4A35),
onSecondaryContainer = Color(0xFF7DFFA0),
tertiary = Color(0xFFFF79C6),
onTertiary = Color(0xFF21222C),
background = Color(0xFF282A36),
onBackground = Color(0xFFF8F8F2),
surface = Color(0xFF21222C),
onSurface = Color(0xFFF8F8F2),
surfaceVariant = Color(0xFF343746),
onSurfaceVariant = Color(0xFFBFBFBF),
outline = Color(0xFF6272A4),
outlineVariant = Color(0xFF44475A),
error = Color(0xFFFF5555),
onError = Color.White,
errorContainer = Color(0xFF4D1A1A),
onErrorContainer = Color(0xFFFF9999)
)
// ── One Dark Pro (Atom-inspired) ──
private val OneDarkProColorScheme =
darkColorScheme(
primary = Color(0xFF61AFEF),
onPrimary = Color(0xFF1E2127),
primaryContainer = Color(0xFF2C3E50),
onPrimaryContainer = Color(0xFF8ECBF7),
secondary = Color(0xFF98C379),
onSecondary = Color(0xFF1E2127),
secondaryContainer = Color(0xFF2D3E2A),
onSecondaryContainer = Color(0xFFB5D99C),
tertiary = Color(0xFFE5C07B),
onTertiary = Color(0xFF1E2127),
background = Color(0xFF282C34),
onBackground = Color(0xFFABB2BF),
surface = Color(0xFF21252B),
onSurface = Color(0xFFABB2BF),
surfaceVariant = Color(0xFF2C313A),
onSurfaceVariant = Color(0xFF8B929E),
outline = Color(0xFF3E4452),
outlineVariant = Color(0xFF333842),
error = Color(0xFFE06C75),
onError = Color.White,
errorContainer = Color(0xFF4D2226),
onErrorContainer = Color(0xFFF0A0A6)
)
// ── Tokyo Night (Neon Deep Blue) ──
private val TokyoNightColorScheme =
darkColorScheme(
primary = Color(0xFF7AA2F7),
onPrimary = Color(0xFF1A1B26),
primaryContainer = Color(0xFF2A3A5E),
onPrimaryContainer = Color(0xFFA9C1FA),
secondary = Color(0xFF9ECE6A),
onSecondary = Color(0xFF1A1B26),
secondaryContainer = Color(0xFF2D3D24),
onSecondaryContainer = Color(0xFFBBE090),
tertiary = Color(0xFFBB9AF7),
onTertiary = Color(0xFF1A1B26),
background = Color(0xFF1A1B26),
onBackground = Color(0xFFC0CAF5),
surface = Color(0xFF16171F),
onSurface = Color(0xFFC0CAF5),
surfaceVariant = Color(0xFF24283B),
onSurfaceVariant = Color(0xFF9AA5CE),
outline = Color(0xFF3B4261),
outlineVariant = Color(0xFF292E42),
error = Color(0xFFF7768E),
onError = Color.White,
errorContainer = Color(0xFF4D2430),
onErrorContainer = Color(0xFFFAA8B6)
)
// ── Nord (Arctic) ──
private val NordDarkColorScheme =
darkColorScheme(
primary = Color(0xFF88C0D0),
onPrimary = Color(0xFF2E3440),
primaryContainer = Color(0xFF3B4C55),
onPrimaryContainer = Color(0xFFA3D4E2),
secondary = Color(0xFFA3BE8C),
onSecondary = Color(0xFF2E3440),
secondaryContainer = Color(0xFF3B4A36),
onSecondaryContainer = Color(0xFFBDD4A8),
tertiary = Color(0xFFEBCB8B),
onTertiary = Color(0xFF2E3440),
background = Color(0xFF2E3440),
onBackground = Color(0xFFECEFF4),
surface = Color(0xFF3B4252),
onSurface = Color(0xFFD8DEE9),
surfaceVariant = Color(0xFF434C5E),
onSurfaceVariant = Color(0xFFD8DEE9),
outline = Color(0xFF4C566A),
outlineVariant = Color(0xFF434C5E),
error = Color(0xFFBF616A),
onError = Color.White,
errorContainer = Color(0xFF4D2528),
onErrorContainer = Color(0xFFE09BA1)
)
// ── Night Owl (Accessibility-focused) ──
private val NightOwlDarkColorScheme =
darkColorScheme(
primary = Color(0xFF7FDBCA),
onPrimary = Color(0xFF011627),
primaryContainer = Color(0xFF1B4A42),
onPrimaryContainer = Color(0xFFA0EDDE),
secondary = Color(0xFFADDB67),
onSecondary = Color(0xFF011627),
secondaryContainer = Color(0xFF2D4A1E),
onSecondaryContainer = Color(0xFFC8EB8E),
tertiary = Color(0xFFC792EA),
onTertiary = Color(0xFF011627),
background = Color(0xFF011627),
onBackground = Color(0xFFD6DEEB),
surface = Color(0xFF0B2942),
onSurface = Color(0xFFD6DEEB),
surfaceVariant = Color(0xFF112B45),
onSurfaceVariant = Color(0xFF9FAFC2),
outline = Color(0xFF1D3B58),
outlineVariant = Color(0xFF15304D),
error = Color(0xFFEF5350),
onError = Color.White,
errorContainer = Color(0xFF4D1A19),
onErrorContainer = Color(0xFFF7A5A3)
)
// ── Anthracite (Material Design Dark) ──
private val AnthraciteDarkColorScheme =
darkColorScheme(
primary = Color(0xFFBB86FC),
onPrimary = Color(0xFF121212),
primaryContainer = Color(0xFF3D2E5C),
onPrimaryContainer = Color(0xFFD4AAFD),
secondary = Color(0xFF03DAC6),
onSecondary = Color(0xFF121212),
secondaryContainer = Color(0xFF0A3D38),
onSecondaryContainer = Color(0xFF4EEADB),
tertiary = Color(0xFFCF6679),
onTertiary = Color(0xFF121212),
background = Color(0xFF121212),
onBackground = Color(0xFFE1E1E1),
surface = Color(0xFF1E1E1E),
onSurface = Color(0xFFE1E1E1),
surfaceVariant = Color(0xFF2C2C2C),
onSurfaceVariant = Color(0xFFAAAAAA),
outline = Color(0xFF3D3D3D),
outlineVariant = Color(0xFF333333),
error = Color(0xFFCF6679),
onError = Color.White,
errorContainer = Color(0xFF4D1F27),
onErrorContainer = Color(0xFFEFA8B4)
)
// ── Cyberpunk (Neon Futuristic) ──
private val CyberpunkDarkColorScheme =
darkColorScheme(
primary = Color(0xFF00FFFF),
onPrimary = Color(0xFF0A0A14),
primaryContainer = Color(0xFF0D3D3D),
onPrimaryContainer = Color(0xFF66FFFF),
secondary = Color(0xFFFF00FF),
onSecondary = Color(0xFF0A0A14),
secondaryContainer = Color(0xFF3D0D3D),
onSecondaryContainer = Color(0xFFFF66FF),
tertiary = Color(0xFFFFFF00),
onTertiary = Color(0xFF0A0A14),
background = Color(0xFF0A0A14),
onBackground = Color(0xFFE0E0F0),
surface = Color(0xFF12121E),
onSurface = Color(0xFFE0E0F0),
surfaceVariant = Color(0xFF1A1A2E),
onSurfaceVariant = Color(0xFFA0A0C0),
outline = Color(0xFF2A2A44),
outlineVariant = Color(0xFF1F1F36),
error = Color(0xFFFF3366),
onError = Color.White,
errorContainer = Color(0xFF4D0F20),
onErrorContainer = Color(0xFFFF8FAB)
)
// ── Navy Elegance (Deep Navy + Gold) ──
private val NavyEleganceDarkColorScheme =
darkColorScheme(
primary = Color(0xFFD4AF37),
onPrimary = Color(0xFF0B1929),
primaryContainer = Color(0xFF3D3218),
onPrimaryContainer = Color(0xFFE8CC6E),
secondary = Color(0xFFC0C0C0),
onSecondary = Color(0xFF0B1929),
secondaryContainer = Color(0xFF2A2D33),
onSecondaryContainer = Color(0xFFD9D9D9),
tertiary = Color(0xFF87CEEB),
onTertiary = Color(0xFF0B1929),
background = Color(0xFF0B1929),
onBackground = Color(0xFFE0E4EA),
surface = Color(0xFF0F2035),
onSurface = Color(0xFFE0E4EA),
surfaceVariant = Color(0xFF162A42),
onSurfaceVariant = Color(0xFFA0AABB),
outline = Color(0xFF1E3550),
outlineVariant = Color(0xFF1A2E46),
error = Color(0xFFE57373),
onError = Color.White,
errorContainer = Color(0xFF4D2222),
onErrorContainer = Color(0xFFF0A8A8)
)
// ── Earthy (Forest & Chocolate) ──
private val EarthyDarkColorScheme =
darkColorScheme(
primary = Color(0xFF81C784),
onPrimary = Color(0xFF1A1510),
primaryContainer = Color(0xFF2E4A2F),
onPrimaryContainer = Color(0xFFA8D8AA),
secondary = Color(0xFFD4A574),
onSecondary = Color(0xFF1A1510),
secondaryContainer = Color(0xFF4A3526),
onSecondaryContainer = Color(0xFFE8C49E),
tertiary = Color(0xFFA5D6A7),
onTertiary = Color(0xFF1A1510),
background = Color(0xFF1A1510),
onBackground = Color(0xFFE0D8CF),
surface = Color(0xFF231E18),
onSurface = Color(0xFFE0D8CF),
surfaceVariant = Color(0xFF2E2720),
onSurfaceVariant = Color(0xFFADA49A),
outline = Color(0xFF3D342B),
outlineVariant = Color(0xFF332C24),
error = Color(0xFFE57373),
onError = Color.White,
errorContainer = Color(0xFF4D2222),
onErrorContainer = Color(0xFFF0A8A8)
)
fun getColorSchemeForTheme(appTheme: AppTheme): androidx.compose.material3.ColorScheme {
return when (appTheme) {
AppTheme.DEFAULT -> DefaultDarkColorScheme
AppTheme.GITHUB -> GitHubDarkColorScheme
AppTheme.LINEAR -> LinearDarkColorScheme
AppTheme.SPOTIFY -> SpotifyDarkColorScheme
AppTheme.NOTION -> NotionDarkColorScheme
AppTheme.DISCORD -> DiscordDarkColorScheme
AppTheme.DRACULA -> DraculaDarkColorScheme
AppTheme.ONE_DARK_PRO -> OneDarkProColorScheme
AppTheme.TOKYO_NIGHT -> TokyoNightColorScheme
AppTheme.NORD -> NordDarkColorScheme
AppTheme.NIGHT_OWL -> NightOwlDarkColorScheme
AppTheme.ANTHRACITE -> AnthraciteDarkColorScheme
AppTheme.CYBERPUNK -> CyberpunkDarkColorScheme
AppTheme.NAVY_ELEGANCE -> NavyEleganceDarkColorScheme
AppTheme.EARTHY -> EarthyDarkColorScheme
}
}
@Composable
fun ShaarItTheme(
appTheme: AppTheme = AppTheme.DEFAULT,
content: @Composable () -> Unit
) {
// Always use the explicit color scheme for the selected theme
val colorScheme = getColorSchemeForTheme(appTheme)
// All custom themes are dark
val isEffectivelyDark = true
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.background.toArgb()
window.navigationBarColor = colorScheme.background.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !isEffectivelyDark
}
}
MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content)
}