Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68c6ecaa authored by mitulsheth's avatar mitulsheth
Browse files

feat:When system is in dark mode ask the user which mode they want to use

parent 6fd5eee3
Loading
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -54,6 +55,7 @@ import earth.maps.cardinal.tileserver.PermissionRequest
import earth.maps.cardinal.tileserver.PermissionRequestManager
import earth.maps.cardinal.ui.core.AppContent
import earth.maps.cardinal.ui.core.MapViewModel
import earth.maps.cardinal.ui.settings.ThemeModePromptBottomSheet
import earth.maps.cardinal.ui.theme.AppTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -226,7 +228,12 @@ class MainActivity : ComponentActivity() {

        setContent {
            val contrastLevel by appPreferenceRepository.contrastLevel.collectAsState()
            AppTheme(contrastLevel = contrastLevel) {
            val themeMode by appPreferenceRepository.themeMode.collectAsState()
            val hasPromptedThemeMode by appPreferenceRepository.hasPromptedThemeMode.collectAsState()
            val systemInDarkTheme = isSystemInDarkTheme()
            val darkTheme = themeMode.shouldUseDarkTheme(systemInDarkTheme)

            AppTheme(darkTheme = darkTheme, contrastLevel = contrastLevel) {
                val mapViewModel: MapViewModel = hiltViewModel()

                val navController = rememberNavController()
@@ -259,6 +266,15 @@ class MainActivity : ComponentActivity() {
                        requestLocationPermission()
                    }
                )

                if (systemInDarkTheme && !hasPromptedThemeMode) {
                    ThemeModePromptBottomSheet(
                        onSave = { selectedThemeMode ->
                            appPreferenceRepository.setThemeMode(selectedThemeMode)
                            appPreferenceRepository.setHasPromptedThemeMode(true)
                        }
                    )
                }
            }
        }
    }
+26 −0
Original line number Diff line number Diff line
@@ -57,6 +57,9 @@ class AppPreferenceRepository @Inject constructor(
    private val _distanceUnit = MutableStateFlow(AppPreferences.DISTANCE_UNIT_METRIC)
    val distanceUnit: StateFlow<Int> = _distanceUnit.asStateFlow()

    private val _themeMode = MutableStateFlow(ThemeMode.SYSTEM)
    val themeMode: StateFlow<ThemeMode> = _themeMode.asStateFlow()

    private val _allowTransitInOfflineMode = MutableStateFlow(true)
    val allowTransitInOfflineMode: StateFlow<Boolean> = _allowTransitInOfflineMode.asStateFlow()

@@ -76,6 +79,9 @@ class AppPreferenceRepository @Inject constructor(
    private val _hasPromptedLocation = MutableStateFlow(appPreferences.loadHasPromptedLocation())
    val hasPromptedLocation: StateFlow<Boolean> = _hasPromptedLocation.asStateFlow()

    private val _hasPromptedThemeMode = MutableStateFlow(appPreferences.loadHasPromptedThemeMode())
    val hasPromptedThemeMode: StateFlow<Boolean> = _hasPromptedThemeMode.asStateFlow()

    // Pelias API configuration
    private val _peliasApiConfig = MutableStateFlow(
        ApiConfiguration(
@@ -99,6 +105,7 @@ class AppPreferenceRepository @Inject constructor(
        loadAnimationSpeed()
        loadOfflineMode()
        loadDistanceUnit()
        loadThemeMode()
        loadAllowTransitInOfflineMode()
        loadContinuousLocationTracking()
        loadShowZoomFabs()
@@ -155,6 +162,18 @@ class AppPreferenceRepository @Inject constructor(
        _distanceUnit.value = distanceUnit
    }

    fun setThemeMode(themeMode: ThemeMode) {
        _themeMode.value = themeMode
        viewModelScope.launch {
            appPreferences.saveThemeMode(themeMode)
        }
    }

    private fun loadThemeMode() {
        val themeMode = appPreferences.loadThemeMode()
        _themeMode.value = themeMode
    }

    private fun loadAllowTransitInOfflineMode() {
        val allowTransitInOfflineMode = appPreferences.loadAllowTransitInOfflineMode()
        _allowTransitInOfflineMode.value = allowTransitInOfflineMode
@@ -222,6 +241,13 @@ class AppPreferenceRepository @Inject constructor(
        }
    }

    fun setHasPromptedThemeMode(hasPrompted: Boolean) {
        _hasPromptedThemeMode.value = hasPrompted
        viewModelScope.launch {
            appPreferences.saveHasPromptedThemeMode(hasPrompted)
        }
    }

    private fun loadApiConfigurations() {
        // Load Pelias configuration
        val peliasBaseUrl = appPreferences.loadPeliasBaseUrl()
+36 −1
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ class AppPreferences(private val context: Context) {
        private const val KEY_USE_24_HOUR_FORMAT = "use_24_hour_format"

        private const val KEY_HAS_PROMPTED_LOCATION = "has_prompted_location"
        private const val KEY_THEME_MODE = "theme_mode"
        private const val KEY_HAS_PROMPTED_THEME_MODE = "has_prompted_theme_mode"

        // API configuration keys
        private const val KEY_PELIAS_BASE_URL = "pelias_base_url"
@@ -75,6 +77,7 @@ class AppPreferences(private val context: Context) {
        // Distance unit constants
        const val DISTANCE_UNIT_METRIC = 0
        const val DISTANCE_UNIT_IMPERIAL = 1

    }

    /**
@@ -305,6 +308,38 @@ class AppPreferences(private val context: Context) {
        return prefs.getBoolean(KEY_HAS_PROMPTED_LOCATION, false)
    }

    fun saveThemeMode(themeMode: ThemeMode) {
        prefs.edit {
            putString(KEY_THEME_MODE, themeMode.preferenceValue)
        }
    }

    fun loadThemeMode(): ThemeMode {
        val themeMode = runCatching {
            prefs.getString(KEY_THEME_MODE, null)
        }.getOrNull()

        if (themeMode != null) {
            return ThemeMode.fromPreferenceValue(themeMode)
        }

        val legacyThemeMode = runCatching {
            prefs.getInt(KEY_THEME_MODE, -1)
        }.getOrNull()

        return legacyThemeMode?.let(ThemeMode::fromLegacyPreferenceValue) ?: ThemeMode.SYSTEM
    }

    fun saveHasPromptedThemeMode(hasPrompted: Boolean) {
        prefs.edit {
            putBoolean(KEY_HAS_PROMPTED_THEME_MODE, hasPrompted)
        }
    }

    fun loadHasPromptedThemeMode(): Boolean {
        return prefs.getBoolean(KEY_HAS_PROMPTED_THEME_MODE, false)
    }

    /**
     * Gets the default distance unit based on the system locale.
     * Returns imperial for countries that use imperial system (US, Liberia, Myanmar),
+3 −1
Original line number Diff line number Diff line
@@ -346,7 +346,7 @@ fun AppContent(
        }

        composable(Screen.TURN_BY_TURN) { backStackEntry ->
            TurnByTurnRoute(state, routeRepository, port, backStackEntry)
            TurnByTurnRoute(state, routeRepository, appPreferenceRepository, port, backStackEntry)
        }
    }

@@ -1095,6 +1095,7 @@ private fun TransitItineraryDetailRoute(
private fun TurnByTurnRoute(
    state: AppContentState,
    routeRepository: RouteRepository,
    appPreferenceRepository: AppPreferenceRepository,
    port: Int?,
    backStackEntry: NavBackStackEntry
) {
@@ -1120,6 +1121,7 @@ private fun TurnByTurnRoute(
            port = port,
            mode = routingMode,
            route = ferrostarRoute,
            appPreferenceRepository = appPreferenceRepository,
        )
    }
}
+9 −7
Original line number Diff line number Diff line
@@ -129,7 +129,9 @@ fun MapView(
    val pinFeatures = mapPins.map { mapViewModel.createFeatureFromPlace(it) }
    rememberCoroutineScope()

    val styleVariant = if (isSystemInDarkTheme()) "dark" else "light"
    val themeMode by appPreferences.themeMode.collectAsState()
    val useDarkTheme = themeMode.shouldUseDarkTheme(isSystemInDarkTheme())
    val styleVariant = if (useDarkTheme) "dark" else "light"

    // Load saved viewport on initial composition
    LaunchedEffect(Unit) {
@@ -186,7 +188,7 @@ fun MapView(
                val location by mapViewModel.locationFlow.collectAsState()
                val sensorHeading by mapViewModel.heading.collectAsState()
                val savedPlaces by mapViewModel.savedPlacesFlow.collectAsState(FeatureCollection())
                FavoritesLayer(savedPlaces, mapPins, isSystemInDarkTheme())
                FavoritesLayer(savedPlaces, mapPins, useDarkTheme)

                OfflineBoundsLayer(selectedOfflineArea)

@@ -194,7 +196,7 @@ fun MapView(

                TransitLayer(currentTransitItinerary)

                PinsLayer(pinFeatures, isSystemInDarkTheme())
                PinsLayer(pinFeatures, useDarkTheme)

                location?.let { LocationPuck(it, sensorHeading) }
            }
@@ -226,7 +228,7 @@ fun MapView(
private fun FavoritesLayer(
    savedPlaces: FeatureCollection<Point, Map<String, JsonElement>>,
    activeMarkers: List<Place>,
    isSystemInDarkTheme: Boolean
    useDarkTheme: Boolean
) {
    val textColor = MaterialTheme.colorScheme.onSurface
    val activeMarkerIds = activeMarkers.mapNotNull { it.id }
@@ -235,7 +237,7 @@ private fun FavoritesLayer(
        source = rememberGeoJsonSource(GeoJsonData.JsonString(Json.encodeToString(savedPlaces))),
        iconAllowOverlap = const(true),
        iconImage = image(
            if (isSystemInDarkTheme) {
            if (useDarkTheme) {
                painterResource(drawable.ic_stars_dark)
            } else {
                painterResource(drawable.ic_stars_light)
@@ -524,14 +526,14 @@ private fun TransitLayer(currentTransitItinerary: Itinerary?) {
}

@Composable
private fun PinsLayer(pinFeatures: List<Feature<Point, Map<String, JsonElement>>>, isSystemInDarkTheme: Boolean) {
private fun PinsLayer(pinFeatures: List<Feature<Point, Map<String, JsonElement>>>, useDarkTheme: Boolean) {
    SymbolLayer(
        id = "map_pins",
        source = rememberGeoJsonSource(GeoJsonData.JsonString(Json.encodeToString(FeatureCollection(features = pinFeatures)))),
        iconAllowOverlap = const(true),
        iconAnchor = const(SymbolAnchor.Bottom),
        iconImage = image(
            if (isSystemInDarkTheme) {
            if (useDarkTheme) {
                painterResource(drawable.map_pin_dark)
            } else {
                painterResource(drawable.map_pin_light)
Loading