Actionable banner når posisjon ikke er tilgjengelig
Tidligere sto statusteksten igjen på «Venter på GPS…» uansett om årsaken var manglende tillatelse, avslåtte stedstjenester eller bare at GPS-en ikke hadde fått fix ennå. For en nødsituasjonsapp er det en reell feilmodus: brukeren får ingen hint om hva som kan gjøres for å finne nærmeste tilfluktsrom. Ny noLocationBanner plasseres øverst i innholdsområdet (rett under statuslinjen) slik at den ikke kolliderer med de flytende handlingsknappene over bunnarket, og viser én av tre tilstander: 1. Tillatelse avslått eller ikke gitt — «Posisjonstilgang nødvendig for å finne nærmeste tilfluktsrom. Du kan også trykke på et merke i kartet.» + «Gi tilgang» som åpner ACTION_APPLICATION_DETAILS_SETTINGS. 2. Tillatelse gitt, men stedstjenester slått av — «Stedstjenester er slått av. Aktiver dem eller velg et tilfluktsrom fra kartet.» + «Aktiver» som åpner ACTION_LOCATION_SOURCE_SETTINGS. 3. Begge OK — banner er skjult og den eksisterende «Venter på GPS…»-teksten gjelder. Helperen updateLocationStatusBanner() kalles fra loadData(), permission-result-kallbacket og onResume(), slik at banneret oppdaterer seg både ved appstart, umiddelbart etter avslag, og når brukeren kommer tilbake fra systeminnstillingene. AlertDialog-en ved permanent avslag er fjernet til fordel for det ikke-modale banneret, som lar brukeren fortsatt pan-ne kartet og velge tilfluktsrom manuelt. Toasten på mykt avslag er beholdt som en kort bekreftelse. API-nivå-fallbacket bruker LocationManager.isLocationEnabled på API 28+, isProviderEnabled for GPS/Network på API 26–27. Verifisert på emulator i alle fire tilstander (avslag → App Settings, tjeneste-av → Posisjonsinnstillinger, gjenopprettet). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7ce0827e9f
commit
f0c4a1f5b4
5 changed files with 97 additions and 22 deletions
|
|
@ -10,9 +10,11 @@ import android.hardware.SensorEvent
|
|||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
|
|
@ -94,28 +96,10 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
|
|||
|
||||
if (fineGranted || coarseGranted) {
|
||||
startLocationUpdates()
|
||||
} else {
|
||||
// Check if user permanently denied (don't show rationale = permanently denied)
|
||||
val shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(
|
||||
this, Manifest.permission.ACCESS_FINE_LOCATION
|
||||
)
|
||||
if (!shouldShowRationale) {
|
||||
// Permission permanently denied — guide user to settings
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.permission_location_title)
|
||||
.setMessage(R.string.permission_denied)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
} else {
|
||||
Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
updateLocationStatusBanner()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -259,6 +243,8 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
|
|||
}
|
||||
|
||||
private fun loadData() {
|
||||
updateLocationStatusBanner()
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
var hasData = repository.hasCachedData()
|
||||
|
|
@ -378,6 +364,44 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
|
|||
)
|
||||
}
|
||||
|
||||
private fun updateLocationStatusBanner() {
|
||||
val banner = binding.noLocationBanner
|
||||
val text = binding.locationBannerText
|
||||
val action = binding.locationBannerAction
|
||||
|
||||
when {
|
||||
!locationProvider.hasLocationPermission() -> {
|
||||
text.setText(R.string.status_location_permission_needed)
|
||||
action.setText(R.string.action_grant_permission)
|
||||
action.setOnClickListener {
|
||||
startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
})
|
||||
}
|
||||
banner.visibility = View.VISIBLE
|
||||
}
|
||||
!isLocationServicesEnabled() -> {
|
||||
text.setText(R.string.status_location_services_off)
|
||||
action.setText(R.string.action_location_settings)
|
||||
action.setOnClickListener {
|
||||
startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
|
||||
}
|
||||
banner.visibility = View.VISIBLE
|
||||
}
|
||||
else -> banner.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLocationServicesEnabled(): Boolean {
|
||||
val lm = getSystemService(Context.LOCATION_SERVICE) as? LocationManager ?: return false
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
lm.isLocationEnabled
|
||||
} else {
|
||||
lm.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
|
||||
lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startLocationUpdates() {
|
||||
// Use repeatOnLifecycle(STARTED) so GPS stops when Activity is paused
|
||||
lifecycleScope.launch {
|
||||
|
|
@ -757,6 +781,10 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
|
|||
binding.mapView.onResume()
|
||||
myLocationOverlay?.enableMyLocation()
|
||||
|
||||
// Re-check permission + location-services state so the banner updates
|
||||
// when the user returns from Settings.
|
||||
updateLocationStatusBanner()
|
||||
|
||||
val sm = sensorManager ?: return
|
||||
|
||||
// Try rotation vector first (best compass source)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@string/a11y_map"
|
||||
app:layout_constraintTop_toBottomOf="@id/statusBar"
|
||||
app:layout_constraintTop_toBottomOf="@id/noLocationBanner"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomSheet" />
|
||||
|
||||
<!-- Direction arrow overlay (shown when toggled) -->
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
android:background="@color/compass_bg"
|
||||
android:contentDescription="@string/a11y_compass"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/statusBar"
|
||||
app:layout_constraintTop_toBottomOf="@id/noLocationBanner"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottomSheet">
|
||||
|
||||
<no.naiv.tilfluktsrom.ui.DirectionArrowView
|
||||
|
|
@ -149,6 +149,41 @@
|
|||
app:backgroundTint="@color/shelter_primary"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<!-- Warning banner: location unavailable (permission denied or services off).
|
||||
Placed at the top of the content area (below the status bar) so it never
|
||||
collides with the floating action buttons anchored above the bottom sheet. -->
|
||||
<LinearLayout
|
||||
android:id="@+id/noLocationBanner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/warning_bg"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/statusBar">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/locationBannerText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/warning_text"
|
||||
android:textSize="12sp"
|
||||
tools:text="@string/status_location_permission_needed" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/locationBannerAction"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:textColor="@color/warning_text"
|
||||
android:textSize="12sp"
|
||||
android:minHeight="0dp"
|
||||
tools:text="@string/action_grant_permission" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Warning banner: no offline map cache -->
|
||||
<LinearLayout
|
||||
android:id="@+id/noCacheBanner"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@
|
|||
<string name="action_cache_now">Lagre nå</string>
|
||||
<string name="action_reset_navigation">Tilbakestill navigasjonsvisning</string>
|
||||
<string name="action_share">Del tilfluktsrom</string>
|
||||
<string name="action_grant_permission">Gi tilgang</string>
|
||||
<string name="action_location_settings">Aktiver</string>
|
||||
<string name="warning_no_map_cache">Ingen frakoblet kart lagret. Kartet krever internett.</string>
|
||||
<string name="status_location_permission_needed">Posisjonstilgang nødvendig for å finne nærmeste tilfluktsrom. Du kan også trykke på et merke i kartet.</string>
|
||||
<string name="status_location_services_off">Stedstjenester er slått av. Aktiver dem eller velg et tilfluktsrom fra kartet.</string>
|
||||
|
||||
<!-- Tillatelser -->
|
||||
<string name="permission_location_title">Posisjonstillatelse kreves</string>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@
|
|||
<string name="action_cache_now">Lagre no</string>
|
||||
<string name="action_reset_navigation">Tilbakestill navigasjonsvising</string>
|
||||
<string name="action_share">Del tilfluktsrom</string>
|
||||
<string name="action_grant_permission">Gje tilgang</string>
|
||||
<string name="action_location_settings">Aktiver</string>
|
||||
<string name="warning_no_map_cache">Ingen fråkopla kart lagra. Kartet krev internett.</string>
|
||||
<string name="status_location_permission_needed">Posisjonstilgang trengst for å finne næraste tilfluktsrom. Du kan òg trykke på eit merke i kartet.</string>
|
||||
<string name="status_location_services_off">Stedstenester er slått av. Aktiver dei eller vel eit tilfluktsrom frå kartet.</string>
|
||||
|
||||
<!-- Løyve -->
|
||||
<string name="permission_location_title">Posisjonsløyve krevst</string>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@
|
|||
<string name="action_cache_now">Cache now</string>
|
||||
<string name="action_reset_navigation">Reset navigation view</string>
|
||||
<string name="action_share">Share shelter</string>
|
||||
<string name="action_grant_permission">Grant access</string>
|
||||
<string name="action_location_settings">Enable</string>
|
||||
<string name="warning_no_map_cache">No offline map cached. Map requires internet.</string>
|
||||
<string name="status_location_permission_needed">Location access needed to find the nearest shelter. You can also tap a marker on the map.</string>
|
||||
<string name="status_location_services_off">Location services are off. Enable them or pick a shelter from the map.</string>
|
||||
|
||||
<!-- Permissions -->
|
||||
<string name="permission_location_title">Location permission required</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue