Compare commits
No commits in common. "ec1c8e69404aed440532bdc108a3b519b53a24e7" and "76ad44a7caae68c76257d93345be78efef266dd3" have entirely different histories.
ec1c8e6940
...
76ad44a7ca
@ -19,8 +19,8 @@
|
|||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@drawable/safebite_logo"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@drawable/safebite_logo"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.SafeBite"
|
android:theme="@style/Theme.SafeBite"
|
||||||
|
|||||||
@ -26,7 +26,6 @@ object UserPreferencesKeys {
|
|||||||
val ONBOARDING_DONE = booleanPreferencesKey("onboarding_done")
|
val ONBOARDING_DONE = booleanPreferencesKey("onboarding_done")
|
||||||
val ACTIVE_PROFILE_IDS = stringSetPreferencesKey("active_profile_ids")
|
val ACTIVE_PROFILE_IDS = stringSetPreferencesKey("active_profile_ids")
|
||||||
val HEALTH_STRICTNESS = stringPreferencesKey("health_strictness")
|
val HEALTH_STRICTNESS = stringPreferencesKey("health_strictness")
|
||||||
val SPLASH_SCREEN_ENABLED = booleanPreferencesKey("splash_screen_enabled")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserPreferences(private val dataStore: DataStore<Preferences>) {
|
class UserPreferences(private val dataStore: DataStore<Preferences>) {
|
||||||
@ -62,10 +61,6 @@ class UserPreferences(private val dataStore: DataStore<Preferences>) {
|
|||||||
.getOrDefault(HealthStrictness.NORMAL)
|
.getOrDefault(HealthStrictness.NORMAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
val splashScreenEnabled: Flow<Boolean> = dataStore.data.map {
|
|
||||||
it[UserPreferencesKeys.SPLASH_SCREEN_ENABLED] ?: true
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun setAppLanguage(value: AppLanguage) {
|
suspend fun setAppLanguage(value: AppLanguage) {
|
||||||
dataStore.edit { it[UserPreferencesKeys.APP_LANGUAGE] = value.name }
|
dataStore.edit { it[UserPreferencesKeys.APP_LANGUAGE] = value.name }
|
||||||
}
|
}
|
||||||
@ -99,8 +94,4 @@ class UserPreferences(private val dataStore: DataStore<Preferences>) {
|
|||||||
suspend fun setHealthStrictness(value: HealthStrictness) {
|
suspend fun setHealthStrictness(value: HealthStrictness) {
|
||||||
dataStore.edit { it[UserPreferencesKeys.HEALTH_STRICTNESS] = value.name }
|
dataStore.edit { it[UserPreferencesKeys.HEALTH_STRICTNESS] = value.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setSplashScreenEnabled(value: Boolean) {
|
|
||||||
dataStore.edit { it[UserPreferencesKeys.SPLASH_SCREEN_ENABLED] = value }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,6 @@ class SettingsRepositoryImpl @Inject constructor(
|
|||||||
override val theme = prefs.theme
|
override val theme = prefs.theme
|
||||||
override val onboardingCompleted = prefs.onboardingCompleted
|
override val onboardingCompleted = prefs.onboardingCompleted
|
||||||
override val healthStrictness = prefs.healthStrictness
|
override val healthStrictness = prefs.healthStrictness
|
||||||
override val splashScreenEnabled = prefs.splashScreenEnabled
|
|
||||||
|
|
||||||
override suspend fun setAppLanguage(value: AppLanguage) = prefs.setAppLanguage(value)
|
override suspend fun setAppLanguage(value: AppLanguage) = prefs.setAppLanguage(value)
|
||||||
override suspend fun setDetectionLanguage(value: DetectionLanguage) = prefs.setDetectionLanguage(value)
|
override suspend fun setDetectionLanguage(value: DetectionLanguage) = prefs.setDetectionLanguage(value)
|
||||||
@ -29,5 +28,4 @@ class SettingsRepositoryImpl @Inject constructor(
|
|||||||
override suspend fun setTheme(value: ThemePref) = prefs.setTheme(value)
|
override suspend fun setTheme(value: ThemePref) = prefs.setTheme(value)
|
||||||
override suspend fun setOnboardingCompleted(value: Boolean) = prefs.setOnboardingCompleted(value)
|
override suspend fun setOnboardingCompleted(value: Boolean) = prefs.setOnboardingCompleted(value)
|
||||||
override suspend fun setHealthStrictness(value: HealthStrictness) = prefs.setHealthStrictness(value)
|
override suspend fun setHealthStrictness(value: HealthStrictness) = prefs.setHealthStrictness(value)
|
||||||
override suspend fun setSplashScreenEnabled(enabled: Boolean) = prefs.setSplashScreenEnabled(enabled)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,7 +57,6 @@ interface SettingsRepository {
|
|||||||
val theme: Flow<ThemePref>
|
val theme: Flow<ThemePref>
|
||||||
val onboardingCompleted: Flow<Boolean>
|
val onboardingCompleted: Flow<Boolean>
|
||||||
val healthStrictness: Flow<HealthStrictness>
|
val healthStrictness: Flow<HealthStrictness>
|
||||||
val splashScreenEnabled: Flow<Boolean>
|
|
||||||
|
|
||||||
suspend fun setAppLanguage(value: AppLanguage)
|
suspend fun setAppLanguage(value: AppLanguage)
|
||||||
suspend fun setDetectionLanguage(value: DetectionLanguage)
|
suspend fun setDetectionLanguage(value: DetectionLanguage)
|
||||||
@ -66,7 +65,6 @@ interface SettingsRepository {
|
|||||||
suspend fun setTheme(value: ThemePref)
|
suspend fun setTheme(value: ThemePref)
|
||||||
suspend fun setOnboardingCompleted(value: Boolean)
|
suspend fun setOnboardingCompleted(value: Boolean)
|
||||||
suspend fun setHealthStrictness(value: HealthStrictness)
|
suspend fun setHealthStrictness(value: HealthStrictness)
|
||||||
suspend fun setSplashScreenEnabled(enabled: Boolean)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|||||||
@ -21,28 +21,14 @@ import kotlinx.coroutines.flow.combine
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
data class RootUi(
|
data class RootUi(val onboardingDone: Boolean = false, val theme: ThemePref = ThemePref.SYSTEM, val ready: Boolean = false)
|
||||||
val onboardingDone: Boolean = false,
|
|
||||||
val theme: ThemePref = ThemePref.SYSTEM,
|
|
||||||
val showSplash: Boolean = false,
|
|
||||||
val ready: Boolean = false
|
|
||||||
)
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class RootViewModel @Inject constructor(
|
class RootViewModel @Inject constructor(
|
||||||
settings: SettingsRepository
|
settings: SettingsRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val state: StateFlow<RootUi> = combine(
|
val state: StateFlow<RootUi> = combine(settings.onboardingCompleted, settings.theme) { done, theme ->
|
||||||
settings.onboardingCompleted,
|
RootUi(onboardingDone = done, theme = theme, ready = true)
|
||||||
settings.theme,
|
|
||||||
settings.splashScreenEnabled
|
|
||||||
) { done, theme, splashEnabled ->
|
|
||||||
RootUi(
|
|
||||||
onboardingDone = done,
|
|
||||||
theme = theme,
|
|
||||||
showSplash = splashEnabled && done,
|
|
||||||
ready = true
|
|
||||||
)
|
|
||||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, RootUi())
|
}.stateIn(viewModelScope, SharingStarted.Eagerly, RootUi())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,10 +49,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
SafeBiteTheme(darkTheme = dark) {
|
SafeBiteTheme(darkTheme = dark) {
|
||||||
if (ui.ready) {
|
if (ui.ready) {
|
||||||
SafeBiteNavGraph(
|
SafeBiteNavGraph(onboardingCompleted = ui.onboardingDone)
|
||||||
onboardingCompleted = ui.onboardingDone,
|
|
||||||
showSplash = ui.showSplash
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import com.safebite.app.presentation.screen.profile.ProfileListScreen
|
|||||||
import com.safebite.app.presentation.screen.result.ResultScreen
|
import com.safebite.app.presentation.screen.result.ResultScreen
|
||||||
import com.safebite.app.presentation.screen.scanner.ScannerScreen
|
import com.safebite.app.presentation.screen.scanner.ScannerScreen
|
||||||
import com.safebite.app.presentation.screen.settings.SettingsScreen
|
import com.safebite.app.presentation.screen.settings.SettingsScreen
|
||||||
import com.safebite.app.presentation.screen.splash.SplashScreen
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Graph de navigation principal de l'application SafeBite.
|
* Graph de navigation principal de l'application SafeBite.
|
||||||
@ -35,13 +34,9 @@ import com.safebite.app.presentation.screen.splash.SplashScreen
|
|||||||
* - Écrans de navigation : Scanner, Result, OCR, Settings, etc.
|
* - Écrans de navigation : Scanner, Result, OCR, Settings, etc.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun SafeBiteNavGraph(onboardingCompleted: Boolean, showSplash: Boolean = false) {
|
fun SafeBiteNavGraph(onboardingCompleted: Boolean) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val startDestination = when {
|
val startDestination = if (onboardingCompleted) Screen.Dashboard.route else Screen.Onboarding.route
|
||||||
showSplash -> Screen.Splash.route
|
|
||||||
onboardingCompleted -> Screen.Dashboard.route
|
|
||||||
else -> Screen.Onboarding.route
|
|
||||||
}
|
|
||||||
|
|
||||||
val enterAnim = fadeIn(animationSpec = tween(250)) +
|
val enterAnim = fadeIn(animationSpec = tween(250)) +
|
||||||
slideInHorizontally(animationSpec = tween(250)) { it / 24 }
|
slideInHorizontally(animationSpec = tween(250)) { it / 24 }
|
||||||
@ -58,17 +53,6 @@ fun SafeBiteNavGraph(onboardingCompleted: Boolean, showSplash: Boolean = false)
|
|||||||
popEnterTransition = { popEnterAnim },
|
popEnterTransition = { popEnterAnim },
|
||||||
popExitTransition = { popExitAnim },
|
popExitTransition = { popExitAnim },
|
||||||
) {
|
) {
|
||||||
// ── Splash ──
|
|
||||||
composable(Screen.Splash.route) {
|
|
||||||
SplashScreen(
|
|
||||||
onFinished = {
|
|
||||||
navController.navigate(Screen.Dashboard.route) {
|
|
||||||
popUpTo(Screen.Splash.route) { inclusive = true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Onboarding ──
|
// ── Onboarding ──
|
||||||
composable(Screen.Onboarding.route) {
|
composable(Screen.Onboarding.route) {
|
||||||
OnboardingScreen(onFinished = {
|
OnboardingScreen(onFinished = {
|
||||||
|
|||||||
@ -39,7 +39,6 @@ sealed class Screen(val route: String) {
|
|||||||
}
|
}
|
||||||
data object Onboarding : Screen("onboarding")
|
data object Onboarding : Screen("onboarding")
|
||||||
data object Settings : Screen("settings")
|
data object Settings : Screen("settings")
|
||||||
data object Splash : Screen("splash")
|
|
||||||
|
|
||||||
// ── Sous-écrans ──
|
// ── Sous-écrans ──
|
||||||
data object ProfileList : Screen("profiles")
|
data object ProfileList : Screen("profiles")
|
||||||
|
|||||||
@ -8,13 +8,9 @@ import androidx.compose.animation.scaleIn
|
|||||||
import androidx.compose.animation.scaleOut
|
import androidx.compose.animation.scaleOut
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.QrCodeScanner
|
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
@ -32,9 +28,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavDestination
|
import androidx.navigation.NavDestination
|
||||||
@ -87,17 +81,7 @@ fun MainScreen(
|
|||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = { Text(stringResource(R.string.app_name)) },
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(id = R.drawable.safebite_logo),
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(32.dp)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
Text(stringResource(R.string.app_name))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = onOpenSettings) {
|
IconButton(onClick = onOpenSettings) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package com.safebite.app.presentation.screen.onboarding
|
package com.safebite.app.presentation.screen.onboarding
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -32,7 +31,6 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
@ -131,12 +129,8 @@ private fun WelcomeStep(onNext: () -> Unit) {
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Image(
|
Text("🛡️", style = MaterialTheme.typography.displayLarge)
|
||||||
painter = painterResource(id = R.drawable.safebite_logo),
|
Spacer(Modifier.height(16.dp))
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(120.dp)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.height(24.dp))
|
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.onboarding_welcome_title),
|
stringResource(R.string.onboarding_welcome_title),
|
||||||
style = MaterialTheme.typography.headlineLarge,
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
|
|||||||
@ -78,7 +78,6 @@ fun SettingsScreen(
|
|||||||
Column(verticalArrangement = Arrangement.spacedBy(dimens.spacingSm)) {
|
Column(verticalArrangement = Arrangement.spacedBy(dimens.spacingSm)) {
|
||||||
ToggleRow(stringResource(R.string.settings_haptics), ui.haptics, viewModel::setHaptics)
|
ToggleRow(stringResource(R.string.settings_haptics), ui.haptics, viewModel::setHaptics)
|
||||||
ToggleRow(stringResource(R.string.settings_sound), ui.sound, viewModel::setSound)
|
ToggleRow(stringResource(R.string.settings_sound), ui.sound, viewModel::setSound)
|
||||||
ToggleRow(stringResource(R.string.settings_splash_screen), ui.splashScreenEnabled, viewModel::setSplashScreenEnabled)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,7 @@ data class SettingsUi(
|
|||||||
val haptics: Boolean = true,
|
val haptics: Boolean = true,
|
||||||
val sound: Boolean = true,
|
val sound: Boolean = true,
|
||||||
val theme: ThemePref = ThemePref.SYSTEM,
|
val theme: ThemePref = ThemePref.SYSTEM,
|
||||||
val healthStrictness: HealthStrictness = HealthStrictness.NORMAL,
|
val healthStrictness: HealthStrictness = HealthStrictness.NORMAL
|
||||||
val splashScreenEnabled: Boolean = true
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@ -44,12 +43,8 @@ class SettingsViewModel @Inject constructor(
|
|||||||
SettingsUi(lang, detection, haptics, sound, theme)
|
SettingsUi(lang, detection, haptics, sound, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
val state: StateFlow<SettingsUi> = combine(
|
val state: StateFlow<SettingsUi> = combine(coreFlow, settings.healthStrictness) { core, strict ->
|
||||||
coreFlow,
|
core.copy(healthStrictness = strict)
|
||||||
settings.healthStrictness,
|
|
||||||
settings.splashScreenEnabled
|
|
||||||
) { core, strict, splash ->
|
|
||||||
core.copy(healthStrictness = strict, splashScreenEnabled = splash)
|
|
||||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), SettingsUi())
|
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), SettingsUi())
|
||||||
|
|
||||||
fun setAppLanguage(v: AppLanguage) = viewModelScope.launch { settings.setAppLanguage(v) }
|
fun setAppLanguage(v: AppLanguage) = viewModelScope.launch { settings.setAppLanguage(v) }
|
||||||
@ -58,7 +53,6 @@ class SettingsViewModel @Inject constructor(
|
|||||||
fun setSound(v: Boolean) = viewModelScope.launch { settings.setSound(v) }
|
fun setSound(v: Boolean) = viewModelScope.launch { settings.setSound(v) }
|
||||||
fun setTheme(v: ThemePref) = viewModelScope.launch { settings.setTheme(v) }
|
fun setTheme(v: ThemePref) = viewModelScope.launch { settings.setTheme(v) }
|
||||||
fun setHealthStrictness(v: HealthStrictness) = viewModelScope.launch { settings.setHealthStrictness(v) }
|
fun setHealthStrictness(v: HealthStrictness) = viewModelScope.launch { settings.setHealthStrictness(v) }
|
||||||
fun setSplashScreenEnabled(v: Boolean) = viewModelScope.launch { settings.setSplashScreenEnabled(v) }
|
|
||||||
fun clearCache() = viewModelScope.launch { productRepo.clearCache() }
|
fun clearCache() = viewModelScope.launch { productRepo.clearCache() }
|
||||||
fun clearHistory() = viewModelScope.launch { historyRepo.clear() }
|
fun clearHistory() = viewModelScope.launch { historyRepo.clear() }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,83 +0,0 @@
|
|||||||
package com.safebite.app.presentation.screen.splash
|
|
||||||
|
|
||||||
import androidx.compose.animation.core.Animatable
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.draw.scale
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.safebite.app.R
|
|
||||||
import com.safebite.app.presentation.theme.ShieldGradient
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun SplashScreen(
|
|
||||||
onFinished: () -> Unit,
|
|
||||||
durationMillis: Int = 2500
|
|
||||||
) {
|
|
||||||
val scale = remember { Animatable(0.6f) }
|
|
||||||
val alpha = remember { Animatable(0f) }
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
scale.animateTo(1f, animationSpec = tween(durationMillis = 800))
|
|
||||||
alpha.animateTo(1f, animationSpec = tween(durationMillis = 600))
|
|
||||||
kotlinx.coroutines.delay(durationMillis.toLong())
|
|
||||||
onFinished()
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.background(ShieldGradient),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier.padding(24.dp)
|
|
||||||
) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(id = R.drawable.safebite_logo),
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(160.dp)
|
|
||||||
.scale(scale.value)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.height(24.dp))
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.app_name),
|
|
||||||
style = MaterialTheme.typography.headlineLarge.copy(
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = androidx.compose.ui.graphics.Color.White
|
|
||||||
),
|
|
||||||
modifier = Modifier.alpha(alpha.value)
|
|
||||||
)
|
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.onboarding_welcome_subtitle),
|
|
||||||
style = MaterialTheme.typography.bodyLarge.copy(
|
|
||||||
color = androidx.compose.ui.graphics.Color.White.copy(alpha = 0.9f)
|
|
||||||
),
|
|
||||||
modifier = Modifier.alpha(alpha.value)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,110 +11,110 @@ import androidx.compose.ui.graphics.Color
|
|||||||
// Ces couleurs sont indépendantes du thème M3 pour cohérence marque.
|
// Ces couleurs sont indépendantes du thème M3 pour cohérence marque.
|
||||||
object SemanticColors {
|
object SemanticColors {
|
||||||
// Light mode
|
// Light mode
|
||||||
val Safe = Color(0xFF43A047) // Vert sécurité
|
val Safe = Color(0xFF2ECC71) // Vert sécurité
|
||||||
val SafeContainer = Color(0xFFE8F5E9) // Fond très clair
|
val SafeContainer = Color(0xFFE8F8F5) // Fond très clair
|
||||||
val OnSafe = Color(0xFFFFFFFF)
|
val OnSafe = Color(0xFFFFFFFF)
|
||||||
val OnSafeContainer = Color(0xFF1A3A2A)
|
val OnSafeContainer = Color(0xFF1A3A2A)
|
||||||
|
|
||||||
val Warning = Color(0xFFFFA000) // Orange attention
|
val Warning = Color(0xFFE67E22) // Orange attention
|
||||||
val WarningContainer = Color(0xFFFFF3E0)
|
val WarningContainer = Color(0xFFFEF5E7)
|
||||||
val OnWarning = Color(0xFFFFFFFF)
|
val OnWarning = Color(0xFFFFFFFF)
|
||||||
val OnWarningContainer = Color(0xFF4A2800)
|
val OnWarningContainer = Color(0xFF3A2A1A)
|
||||||
|
|
||||||
val Danger = Color(0xFFD32F2F) // Rouge danger
|
val Danger = Color(0xFFE74C3C) // Rouge danger
|
||||||
val DangerContainer = Color(0xFFFFEBEE)
|
val DangerContainer = Color(0xFFFDEDEC)
|
||||||
val OnDanger = Color(0xFFFFFFFF)
|
val OnDanger = Color(0xFFFFFFFF)
|
||||||
val OnDangerContainer = Color(0xFF5C0B0B)
|
val OnDangerContainer = Color(0xFF3A1A1A)
|
||||||
|
|
||||||
// Dark mode (mêmes couleurs sémantiques, containers adaptés)
|
// Dark mode (mêmes couleurs sémantiques, containers adaptés)
|
||||||
val SafeDark = Color(0xFF81C784)
|
val SafeDark = Color(0xFF2ECC71)
|
||||||
val SafeContainerDark = Color(0xFF1B5E20)
|
val SafeContainerDark = Color(0xFF1A3A2A)
|
||||||
val WarningDark = Color(0xFFFFB74D)
|
val WarningDark = Color(0xFFE67E22)
|
||||||
val WarningContainerDark = Color(0xFF4A2800)
|
val WarningContainerDark = Color(0xFF3A2A1A)
|
||||||
val DangerDark = Color(0xFFEF9A9A)
|
val DangerDark = Color(0xFFE74C3C)
|
||||||
val DangerContainerDark = Color(0xFF5C0B0B)
|
val DangerContainerDark = Color(0xFF3A1A1A)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- NEUTRES (spec UX §2.1) ------------------------------------------------
|
// ---- NEUTRES (spec UX §2.1) ------------------------------------------------
|
||||||
object NeutralColors {
|
object NeutralColors {
|
||||||
val Background = Color(0xFFF1F8E9) // Fond principal light
|
val Background = Color(0xFFF5F5F0) // Gris chaud (réduit fatigue oculaire)
|
||||||
val Surface = Color(0xFFFFFFFF) // Blanc pur pour cartes
|
val Surface = Color(0xFFFFFFFF) // Blanc pur pour cartes
|
||||||
val TextPrimary = Color(0xFF212121) // Texte principal
|
val TextPrimary = Color(0xFF2D3436) // Noir doux (pas #000)
|
||||||
val TextSecondary = Color(0xFF757575) // Texte secondaire
|
val TextSecondary = Color(0xFF636E72) // Gris moyen
|
||||||
val Separator = Color(0xFFBDBDBD) // Séparateurs
|
val Separator = Color(0xFFDFE6E9) // Gris clair
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Brand anchors (Material 3) --------------------------------------------
|
// ---- Brand anchors (Material 3) --------------------------------------------
|
||||||
val BrandPrimary = Color(0xFF1B7A2B)
|
val BrandIndigo = Color(0xFF1A237E)
|
||||||
val BrandPrimaryDark = Color(0xFF0D5E1A)
|
val BrandIndigoLight = Color(0xFFBAC3FF)
|
||||||
val BrandPrimaryLight = Color(0xFF4CAF50)
|
val BrandTeal = Color(0xFF00897B)
|
||||||
val BrandSecondary = Color(0xFF2E7D32)
|
val BrandTealLight = Color(0xFF4DB6AC)
|
||||||
|
|
||||||
// ---- Light scheme ---------------------------------------------------------
|
// ---- Light scheme ---------------------------------------------------------
|
||||||
val LightPrimary = Color(0xFF1B7A2B)
|
val LightPrimary = Color(0xFF1A237E)
|
||||||
val LightOnPrimary = Color(0xFFFFFFFF)
|
val LightOnPrimary = Color(0xFFFFFFFF)
|
||||||
val LightPrimaryContainer = Color(0xFFA5D6A7)
|
val LightPrimaryContainer = Color(0xFFDDE1FF)
|
||||||
val LightOnPrimaryContainer = Color(0xFF0D3B12)
|
val LightOnPrimaryContainer = Color(0xFF001159)
|
||||||
|
|
||||||
val LightSecondary = Color(0xFF2E7D32)
|
val LightSecondary = Color(0xFF00897B)
|
||||||
val LightOnSecondary = Color(0xFFFFFFFF)
|
val LightOnSecondary = Color(0xFFFFFFFF)
|
||||||
val LightSecondaryContainer = Color(0xFFC8E6C9)
|
val LightSecondaryContainer = Color(0xFFB2DFDB)
|
||||||
val LightOnSecondaryContainer = Color(0xFF1B5E20)
|
val LightOnSecondaryContainer = Color(0xFF00251F)
|
||||||
|
|
||||||
val LightTertiary = Color(0xFF00796B)
|
val LightTertiary = Color(0xFF7B4E9E)
|
||||||
val LightOnTertiary = Color(0xFFFFFFFF)
|
val LightOnTertiary = Color(0xFFFFFFFF)
|
||||||
val LightTertiaryContainer = Color(0xFFB2DFDB)
|
val LightTertiaryContainer = Color(0xFFF0DBFF)
|
||||||
val LightOnTertiaryContainer = Color(0xFF004D40)
|
val LightOnTertiaryContainer = Color(0xFF2B0A45)
|
||||||
|
|
||||||
val LightError = Color(0xFFD32F2F)
|
val LightError = Color(0xFFBA1A1A)
|
||||||
val LightOnError = Color(0xFFFFFFFF)
|
val LightOnError = Color(0xFFFFFFFF)
|
||||||
val LightErrorContainer = Color(0xFFFFCDD2)
|
val LightErrorContainer = Color(0xFFFFDAD6)
|
||||||
val LightOnErrorContainer = Color(0xFF5C0B0B)
|
val LightOnErrorContainer = Color(0xFF410002)
|
||||||
|
|
||||||
val LightBackground = NeutralColors.Background // #F1F8E9
|
val LightBackground = NeutralColors.Background // #F5F5F0
|
||||||
val LightOnBackground = NeutralColors.TextPrimary // #212121
|
val LightOnBackground = NeutralColors.TextPrimary // #2D3436
|
||||||
val LightSurface = NeutralColors.Surface // #FFFFFF
|
val LightSurface = NeutralColors.Surface // #FFFFFF
|
||||||
val LightOnSurface = NeutralColors.TextPrimary // #212121
|
val LightOnSurface = NeutralColors.TextPrimary // #2D3436
|
||||||
val LightSurfaceVariant = Color(0xFFE8F5E9)
|
val LightSurfaceVariant = Color(0xFFE3E2EC)
|
||||||
val LightOnSurfaceVariant = NeutralColors.TextSecondary
|
val LightOnSurfaceVariant = NeutralColors.TextSecondary
|
||||||
val LightSurfaceTint = LightPrimary
|
val LightSurfaceTint = LightPrimary
|
||||||
|
|
||||||
val LightOutline = NeutralColors.Separator
|
val LightOutline = NeutralColors.Separator
|
||||||
val LightOutlineVariant = Color(0xFFE0E0E0)
|
val LightOutlineVariant = Color(0xFFC7C6D0)
|
||||||
|
|
||||||
val LightInverseSurface = Color(0xFF2F3033)
|
val LightInverseSurface = Color(0xFF2F3033)
|
||||||
val LightInverseOnSurface = Color(0xFFF1F0F4)
|
val LightInverseOnSurface = Color(0xFFF1F0F4)
|
||||||
val LightInversePrimary = Color(0xFF81C784)
|
val LightInversePrimary = Color(0xFFBAC3FF)
|
||||||
|
|
||||||
val LightScrim = Color(0xFF000000)
|
val LightScrim = Color(0xFF000000)
|
||||||
|
|
||||||
// ---- Dark scheme (surfaces élevées M3) ------------------------------------
|
// ---- Dark scheme (surfaces élevées M3) ------------------------------------
|
||||||
val DarkPrimary = Color(0xFF81C784)
|
val DarkPrimary = Color(0xFFBAC3FF)
|
||||||
val DarkOnPrimary = Color(0xFF0D3B12)
|
val DarkOnPrimary = Color(0xFF0A1A6A)
|
||||||
val DarkPrimaryContainer = Color(0xFF1B5E20)
|
val DarkPrimaryContainer = Color(0xFF3241A0)
|
||||||
val DarkOnPrimaryContainer = Color(0xFFA5D6A7)
|
val DarkOnPrimaryContainer = Color(0xFFDDE1FF)
|
||||||
|
|
||||||
val DarkSecondary = Color(0xFFA5D6A7)
|
val DarkSecondary = Color(0xFF4DB6AC)
|
||||||
val DarkOnSecondary = Color(0xFF1B5E20)
|
val DarkOnSecondary = Color(0xFF00332C)
|
||||||
val DarkSecondaryContainer = Color(0xFF2E7D32)
|
val DarkSecondaryContainer = Color(0xFF00695C)
|
||||||
val DarkOnSecondaryContainer = Color(0xFFC8E6C9)
|
val DarkOnSecondaryContainer = Color(0xFFB2DFDB)
|
||||||
|
|
||||||
val DarkTertiary = Color(0xFF4DB6AC)
|
val DarkTertiary = Color(0xFFE0B6FF)
|
||||||
val DarkOnTertiary = Color(0xFF00332C)
|
val DarkOnTertiary = Color(0xFF451F6D)
|
||||||
val DarkTertiaryContainer = Color(0xFF00695C)
|
val DarkTertiaryContainer = Color(0xFF5D3785)
|
||||||
val DarkOnTertiaryContainer = Color(0xFFB2DFDB)
|
val DarkOnTertiaryContainer = Color(0xFFF0DBFF)
|
||||||
|
|
||||||
val DarkError = Color(0xFFEF9A9A)
|
val DarkError = Color(0xFFFFB4AB)
|
||||||
val DarkOnError = Color(0xFF690005)
|
val DarkOnError = Color(0xFF690005)
|
||||||
val DarkErrorContainer = Color(0xFF93000A)
|
val DarkErrorContainer = Color(0xFF93000A)
|
||||||
val DarkOnErrorContainer = Color(0xFFFFCDD2)
|
val DarkOnErrorContainer = Color(0xFFFFDAD6)
|
||||||
|
|
||||||
val DarkBackground = Color(0xFF1A1C1A)
|
val DarkBackground = Color(0xFF121212)
|
||||||
val DarkOnBackground = Color(0xFFE0E0E0)
|
val DarkOnBackground = Color(0xFFE6E1E5)
|
||||||
val DarkSurface = Color(0xFF2D2F2D)
|
val DarkSurface = Color(0xFF1E1E1E)
|
||||||
val DarkOnSurface = Color(0xFFE0E0E0)
|
val DarkOnSurface = Color(0xFFE6E1E5)
|
||||||
val DarkSurfaceVariant = Color(0xFF3A3F3A)
|
val DarkSurfaceVariant = Color(0xFF46464F)
|
||||||
val DarkOnSurfaceVariant = Color(0xFFBDBDBD)
|
val DarkOnSurfaceVariant = Color(0xFFC7C6D0)
|
||||||
val DarkSurfaceTint = DarkPrimary
|
val DarkSurfaceTint = DarkPrimary
|
||||||
|
|
||||||
val DarkOutline = Color(0xFF90909A)
|
val DarkOutline = Color(0xFF90909A)
|
||||||
@ -122,21 +122,10 @@ val DarkOutlineVariant = Color(0xFF46464F)
|
|||||||
|
|
||||||
val DarkInverseSurface = Color(0xFFE6E1E5)
|
val DarkInverseSurface = Color(0xFFE6E1E5)
|
||||||
val DarkInverseOnSurface = Color(0xFF2F3033)
|
val DarkInverseOnSurface = Color(0xFF2F3033)
|
||||||
val DarkInversePrimary = Color(0xFF1B7A2B)
|
val DarkInversePrimary = Color(0xFF1A237E)
|
||||||
|
|
||||||
val DarkScrim = Color(0xFF000000)
|
val DarkScrim = Color(0xFF000000)
|
||||||
|
|
||||||
// ---- Dégradé signature (rappel du fond du logo bouclier) --------------------
|
|
||||||
val ShieldGradient = androidx.compose.ui.graphics.Brush.linearGradient(
|
|
||||||
colors = listOf(
|
|
||||||
Color(0xFF4CAF50), // Vert clair (haut-gauche)
|
|
||||||
Color(0xFF1B7A2B), // Vert moyen
|
|
||||||
Color(0xFF0D5E1A) // Vert foncé (bas-droite)
|
|
||||||
),
|
|
||||||
start = androidx.compose.ui.geometry.Offset(0f, 0f),
|
|
||||||
end = androidx.compose.ui.geometry.Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
|
|
||||||
)
|
|
||||||
|
|
||||||
// ---- Legacy aliases (backward compat pour code existant) -------------------
|
// ---- Legacy aliases (backward compat pour code existant) -------------------
|
||||||
@Deprecated("Use SemanticColors.Safe", ReplaceWith("SemanticColors.Safe"))
|
@Deprecated("Use SemanticColors.Safe", ReplaceWith("SemanticColors.Safe"))
|
||||||
val StatusSafe get() = SemanticColors.Safe
|
val StatusSafe get() = SemanticColors.Safe
|
||||||
|
|||||||
@ -8,126 +8,128 @@ import androidx.compose.ui.unit.sp
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Typographie Material 3 complète — 15 styles avec lineHeight et letterSpacing
|
* Typographie Material 3 complète — 15 styles avec lineHeight et letterSpacing
|
||||||
* standardisés. Alignée sur le guide design SafeBite (Poppins titres, Inter corps).
|
* standardisés. Utilise la sans-serif système (Roboto sur Android) pour éviter
|
||||||
* Utilise la sans-serif système (Roboto sur Android) pour éviter
|
|
||||||
* toute dépendance réseau / asset.
|
* toute dépendance réseau / asset.
|
||||||
*/
|
*/
|
||||||
private val PoppinsFamily = FontFamily.SansSerif
|
private val DisplayFamily = FontFamily.SansSerif
|
||||||
private val InterFamily = FontFamily.SansSerif
|
private val HeadlineFamily = FontFamily.SansSerif
|
||||||
|
private val TitleFamily = FontFamily.SansSerif
|
||||||
|
private val BodyFamily = FontFamily.SansSerif
|
||||||
|
private val LabelFamily = FontFamily.SansSerif
|
||||||
|
|
||||||
val SafeBiteTypography = Typography(
|
val SafeBiteTypography = Typography(
|
||||||
// Display — pour les titres héros (onboarding, banner)
|
// Display — pour les titres héros (onboarding, banner)
|
||||||
displayLarge = TextStyle(
|
displayLarge = TextStyle(
|
||||||
fontFamily = PoppinsFamily,
|
fontFamily = DisplayFamily,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 57.sp,
|
fontSize = 57.sp,
|
||||||
lineHeight = 64.sp,
|
lineHeight = 64.sp,
|
||||||
letterSpacing = (-0.25).sp
|
letterSpacing = (-0.25).sp
|
||||||
),
|
),
|
||||||
displayMedium = TextStyle(
|
displayMedium = TextStyle(
|
||||||
fontFamily = PoppinsFamily,
|
fontFamily = DisplayFamily,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 45.sp,
|
fontSize = 45.sp,
|
||||||
lineHeight = 52.sp,
|
lineHeight = 52.sp,
|
||||||
letterSpacing = 0.sp
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
displaySmall = TextStyle(
|
displaySmall = TextStyle(
|
||||||
fontFamily = PoppinsFamily,
|
fontFamily = DisplayFamily,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 36.sp,
|
fontSize = 36.sp,
|
||||||
lineHeight = 44.sp,
|
lineHeight = 44.sp,
|
||||||
letterSpacing = 0.sp
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
|
|
||||||
// Headline — sections majeures (Poppins Bold/SemiBold)
|
// Headline — sections majeures
|
||||||
headlineLarge = TextStyle(
|
headlineLarge = TextStyle(
|
||||||
fontFamily = PoppinsFamily,
|
fontFamily = HeadlineFamily,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 32.sp,
|
fontSize = 32.sp,
|
||||||
lineHeight = 40.sp,
|
lineHeight = 40.sp,
|
||||||
letterSpacing = (-0.5).sp
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
headlineMedium = TextStyle(
|
headlineMedium = TextStyle(
|
||||||
fontFamily = PoppinsFamily,
|
fontFamily = HeadlineFamily,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 28.sp,
|
||||||
|
lineHeight = 36.sp,
|
||||||
|
letterSpacing = 0.sp
|
||||||
|
),
|
||||||
|
headlineSmall = TextStyle(
|
||||||
|
fontFamily = HeadlineFamily,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
lineHeight = 32.sp,
|
lineHeight = 32.sp,
|
||||||
letterSpacing = 0.sp
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
headlineSmall = TextStyle(
|
|
||||||
fontFamily = PoppinsFamily,
|
|
||||||
fontWeight = FontWeight.SemiBold,
|
|
||||||
fontSize = 20.sp,
|
|
||||||
lineHeight = 28.sp,
|
|
||||||
letterSpacing = 0.15.sp
|
|
||||||
),
|
|
||||||
|
|
||||||
// Title — titres d'écran, de cartes (Inter Medium)
|
// Title — titres d'écran, de cartes
|
||||||
titleLarge = TextStyle(
|
titleLarge = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = TitleFamily,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.SemiBold,
|
||||||
fontSize = 22.sp,
|
fontSize = 22.sp,
|
||||||
lineHeight = 28.sp,
|
lineHeight = 28.sp,
|
||||||
letterSpacing = 0.sp
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
titleMedium = TextStyle(
|
titleMedium = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = TitleFamily,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.SemiBold,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
lineHeight = 24.sp,
|
lineHeight = 24.sp,
|
||||||
letterSpacing = 0.15.sp
|
letterSpacing = 0.15.sp
|
||||||
),
|
),
|
||||||
titleSmall = TextStyle(
|
titleSmall = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = TitleFamily,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
lineHeight = 20.sp,
|
lineHeight = 20.sp,
|
||||||
letterSpacing = 0.1.sp
|
letterSpacing = 0.1.sp
|
||||||
),
|
),
|
||||||
|
|
||||||
// Body — textes courants (Inter Regular)
|
// Body — textes courants
|
||||||
bodyLarge = TextStyle(
|
bodyLarge = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = BodyFamily,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
lineHeight = 24.sp,
|
lineHeight = 24.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp
|
||||||
),
|
),
|
||||||
bodyMedium = TextStyle(
|
bodyMedium = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = BodyFamily,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
lineHeight = 20.sp,
|
lineHeight = 20.sp,
|
||||||
letterSpacing = 0.25.sp
|
letterSpacing = 0.25.sp
|
||||||
),
|
),
|
||||||
bodySmall = TextStyle(
|
bodySmall = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = BodyFamily,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
letterSpacing = 0.4.sp
|
letterSpacing = 0.4.sp
|
||||||
),
|
),
|
||||||
|
|
||||||
// Label — boutons, chips, tags (Poppins SemiBold pour boutons, Inter Medium pour petits)
|
// Label — boutons, chips, tags
|
||||||
labelLarge = TextStyle(
|
labelLarge = TextStyle(
|
||||||
fontFamily = PoppinsFamily,
|
fontFamily = LabelFamily,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
lineHeight = 20.sp,
|
lineHeight = 20.sp,
|
||||||
letterSpacing = 1.25.sp
|
letterSpacing = 0.1.sp
|
||||||
),
|
),
|
||||||
labelMedium = TextStyle(
|
labelMedium = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = LabelFamily,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp
|
||||||
),
|
),
|
||||||
labelSmall = TextStyle(
|
labelSmall = TextStyle(
|
||||||
fontFamily = InterFamily,
|
fontFamily = LabelFamily,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 10.sp,
|
fontSize = 11.sp,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
letterSpacing = 1.5.sp
|
letterSpacing = 0.5.sp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 912 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.6 MiB |
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:src="@drawable/safebite_logo"
|
|
||||||
android:gravity="center" />
|
|
||||||
|
Before Width: | Height: | Size: 873 KiB |
@ -1,88 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright (C) 2026 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
<vector
|
|
||||||
android:height="108dp"
|
|
||||||
android:width="108dp"
|
|
||||||
android:viewportHeight="108"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="#3DDC84"
|
|
||||||
android:pathData="M0,0h108v108h-108z"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
|
||||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
|
||||||
</vector>
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright (C) 2026 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path
|
|
||||||
android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:startY="49.59793"
|
|
||||||
android:startX="42.9492"
|
|
||||||
android:endY="92.4963"
|
|
||||||
android:endX="85.84757"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000"/>
|
|
||||||
</vector>
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright (C) 2026 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/safebite_logo_background"/>
|
|
||||||
<foreground android:drawable="@drawable/safebite_logo"/>
|
|
||||||
<monochrome android:drawable="@drawable/safebite_logo"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright (C) 2026 The Android Open Source Project
|
|
||||||
~
|
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
~ you may not use this file except in compliance with the License.
|
|
||||||
~ You may obtain a copy of the License at
|
|
||||||
~
|
|
||||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ Unless required by applicable law or agreed to in writing, software
|
|
||||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
~ See the License for the specific language governing permissions and
|
|
||||||
~ limitations under the License.
|
|
||||||
-->
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/safebite_logo_background"/>
|
|
||||||
<foreground android:drawable="@drawable/safebite_logo_foreground"/>
|
|
||||||
<monochrome android:drawable="@drawable/safebite_logo_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@ -122,7 +122,6 @@
|
|||||||
<string name="settings_theme_system">System</string>
|
<string name="settings_theme_system">System</string>
|
||||||
<string name="settings_clear_cache">Clear product cache</string>
|
<string name="settings_clear_cache">Clear product cache</string>
|
||||||
<string name="settings_clear_history">Clear history</string>
|
<string name="settings_clear_history">Clear history</string>
|
||||||
<string name="settings_splash_screen">Splash screen</string>
|
|
||||||
<string name="settings_about">About</string>
|
<string name="settings_about">About</string>
|
||||||
<string name="settings_version">Version %1$s</string>
|
<string name="settings_version">Version %1$s</string>
|
||||||
<string name="settings_off_attribution">Data provided by Open Food Facts</string>
|
<string name="settings_off_attribution">Data provided by Open Food Facts</string>
|
||||||
|
|||||||
@ -190,7 +190,6 @@
|
|||||||
<string name="settings_theme_system">Système</string>
|
<string name="settings_theme_system">Système</string>
|
||||||
<string name="settings_clear_cache">Vider le cache des produits</string>
|
<string name="settings_clear_cache">Vider le cache des produits</string>
|
||||||
<string name="settings_clear_history">Vider l\'historique</string>
|
<string name="settings_clear_history">Vider l\'historique</string>
|
||||||
<string name="settings_splash_screen">Écran de démarrage (splash)</string>
|
|
||||||
<string name="settings_about">À propos</string>
|
<string name="settings_about">À propos</string>
|
||||||
<string name="settings_version">Version %1$s</string>
|
<string name="settings_version">Version %1$s</string>
|
||||||
<string name="settings_off_attribution">Données fournies par Open Food Facts</string>
|
<string name="settings_off_attribution">Données fournies par Open Food Facts</string>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 40 KiB |
@ -1,13 +0,0 @@
|
|||||||
#This file is generated by updateDaemonJvm
|
|
||||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
|
|
||||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
|
|
||||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
|
|
||||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
|
|
||||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e99bae143b75f9a10ead10248f02055e/redirect
|
|
||||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/04e088f8677de3b384108493cc9481d0/redirect
|
|
||||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
|
|
||||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
|
|
||||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e55dccbfe27cb97945148c61a39c89c5/redirect
|
|
||||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/dbd05c4936d573642f94cd149e1356c8/redirect
|
|
||||||
toolchainVendor=JETBRAINS
|
|
||||||
toolchainVersion=21
|
|
||||||
@ -11,9 +11,6 @@ pluginManagement {
|
|||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
plugins {
|
|
||||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
MAJOR=1
|
MAJOR=1
|
||||||
MINOR=14
|
MINOR=9
|
||||||
PATCH=0
|
PATCH=2
|
||||||
CODE=18
|
CODE=13
|
||||||
|
|||||||