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

Commit 471dbc78 authored by Ellen Poe's avatar Ellen Poe
Browse files

Merge branch 'ellenhp/permission_on_startup' into 'main'

Prompt for location permission on startup

See merge request e/os/cardinal!18
parents 832c1a87 19194acf
Loading
Loading
Loading
Loading
Loading
+25 −1
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ class MainActivity : ComponentActivity() {
    private var hasLocationPermission by mutableStateOf(false)
    private var hasNotificationPermission by mutableStateOf(false)
    private var deepLinkDestination by mutableStateOf<String?>(null)
    private var showLocationPermissionDialog by mutableStateOf(false)

    companion object {
        private const val LOCATION_PERMISSION_REQUEST_CODE = 1001
@@ -157,6 +158,14 @@ class MainActivity : ComponentActivity() {
        hasLocationPermission = isGranted
        if (isGranted) {
            Log.d(TAG, "Location permission granted")
            // Request fresh location and animate camera to user's location
            lifecycleScope.launch {
                locationRepository.getCurrentLocation(this@MainActivity)?.let { location ->
                    Log.d(TAG, "Got location: ${location.latitude}, ${location.longitude}")
                    // The MapViewModel will handle the camera animation through its 
                    // handlePermissionStateChange method
                }
            }
        } else {
            Log.d(TAG, "Location permission denied")
        }
@@ -193,6 +202,11 @@ class MainActivity : ComponentActivity() {
        hasNotificationPermission = checkNotificationPermission()
        hasLocationPermission = checkLocationPermission()

        // Check if we should show the location permission dialog on first startup
        if (!appPreferenceRepository.hasPromptedLocation.value && !hasLocationPermission) {
            showLocationPermissionDialog = true
        }

        CoroutineScope(Dispatchers.IO).launch {
            migrationHelper.migratePlacesToSavedPlaces()
            savedListRepository.cleanupUnparentedElements()
@@ -233,7 +247,17 @@ class MainActivity : ComponentActivity() {
                    routeRepository = routeRepository,
                    appPreferenceRepository = appPreferenceRepository,
                    onRequestNotificationPermission = { requestNotificationPermission() },
                    hasNotificationPermission = hasNotificationPermission
                    hasNotificationPermission = hasNotificationPermission,
                    showLocationPermissionDialog = showLocationPermissionDialog,
                    onDismissLocationDialog = {
                        showLocationPermissionDialog = false
                        appPreferenceRepository.setHasPromptedLocation(true)
                    },
                    onAcceptLocationDialog = {
                        showLocationPermissionDialog = false
                        appPreferenceRepository.setHasPromptedLocation(true)
                        requestLocationPermission()
                    }
                )
            }
        }
+10 −0
Original line number Diff line number Diff line
@@ -73,6 +73,9 @@ class AppPreferenceRepository @Inject constructor(
    private val _lastRoutingMode = MutableStateFlow(appPreferences.loadLastRoutingMode())
    val lastRoutingMode: StateFlow<String> = _lastRoutingMode.asStateFlow()

    private val _hasPromptedLocation = MutableStateFlow(appPreferences.loadHasPromptedLocation())
    val hasPromptedLocation: StateFlow<Boolean> = _hasPromptedLocation.asStateFlow()

    // Pelias API configuration
    private val _peliasApiConfig = MutableStateFlow(
        ApiConfiguration(
@@ -212,6 +215,13 @@ class AppPreferenceRepository @Inject constructor(
        }
    }

    fun setHasPromptedLocation(hasPrompted: Boolean) {
        _hasPromptedLocation.value = hasPrompted
        viewModelScope.launch {
            appPreferences.saveHasPromptedLocation(hasPrompted)
        }
    }

    private fun loadApiConfigurations() {
        // Load Pelias configuration
        val peliasBaseUrl = appPreferences.loadPeliasBaseUrl()
+19 −0
Original line number Diff line number Diff line
@@ -46,6 +46,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"

        // API configuration keys
        private const val KEY_PELIAS_BASE_URL = "pelias_base_url"
        private const val KEY_PELIAS_API_KEY = "pelias_api_key"
@@ -287,6 +289,23 @@ class AppPreferences(private val context: Context) {
        return prefs.getBoolean(KEY_USE_24_HOUR_FORMAT, systemDefault)
    }

    /**
     * Saves whether the user has been prompted for location permission.
     */
    fun saveHasPromptedLocation(hasPrompted: Boolean) {
        prefs.edit {
            putBoolean(KEY_HAS_PROMPTED_LOCATION, hasPrompted)
        }
    }

    /**
     * Loads whether the user has been prompted for location permission.
     * Returns false as default (user has not been prompted yet).
     */
    fun loadHasPromptedLocation(): Boolean {
        return prefs.getBoolean(KEY_HAS_PROMPTED_LOCATION, false)
    }

    /**
     * Gets the default distance unit based on the system locale.
     * Returns imperial for countries that use imperial system (US, Liberia, Myanmar),
+43 −0
Original line number Diff line number Diff line
@@ -150,6 +150,9 @@ fun AppContent(
    hasNotificationPermission: Boolean,
    routeRepository: RouteRepository,
    appPreferenceRepository: AppPreferenceRepository,
    showLocationPermissionDialog: Boolean = false,
    onDismissLocationDialog: () -> Unit = {},
    onAcceptLocationDialog: () -> Unit = {},
    state: AppContentState = rememberAppContentState(),
) {

@@ -380,6 +383,19 @@ fun AppContent(
            CardinalToolbar(navController, onSearchDoublePress = { homeViewModel.expandSearch() })
        }
    }

    // Show location permission dialog on first startup
    if (showLocationPermissionDialog) {
        LocationPermissionDialog(
            onDismiss = onDismissLocationDialog,
            onAccept = {
                // Mark that we have a pending location request so the camera will animate
                // when permission is granted
                mapViewModel.markLocationRequestPending()
                onAcceptLocationDialog()
            }
        )
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@@ -1243,3 +1259,30 @@ fun BirdSettingsFab(navController: NavController) {
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LocationPermissionDialog(
    onDismiss: () -> Unit,
    onAccept: () -> Unit
) {
    androidx.compose.material3.AlertDialog(
        onDismissRequest = onDismiss,
        title = {
            androidx.compose.material3.Text(text = stringResource(string.enable_location_title))
        },
        text = {
            androidx.compose.material3.Text(text = stringResource(string.enable_location_message))
        },
        confirmButton = {
            androidx.compose.material3.TextButton(onClick = onAccept) {
                androidx.compose.material3.Text(text = stringResource(string.allow))
            }
        },
        dismissButton = {
            androidx.compose.material3.TextButton(onClick = onDismiss) {
                androidx.compose.material3.Text(text = stringResource(string.not_now))
            }
        }
    )
}
+6 −0
Original line number Diff line number Diff line
@@ -248,4 +248,10 @@
    <string name="zoom_out">Zoom out</string>
    <string name="searching">Searching…</string>
    <string name="remove_recent_search">Remove recent search</string>
    
    <!-- Location Permission Dialog -->
    <string name="enable_location_title">Enable Location?</string>
    <string name="enable_location_message">Allow Cardinal Maps to access your location to show you where you are on the map and provide a better experience.</string>
    <string name="allow">Allow</string>
    <string name="not_now">Not Now</string>
</resources>