Compare commits

...

10 commits

Author SHA1 Message Date
12b83993c4 Bump versjon til v1.4.1 (versionCode 7)
Tilgjenge-, brukarvenlegheit- og metadataforbetringar sidan v1.4.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:21:22 +01:00
e9432756b0 Førebu F-Droid-metadata med fastlane-struktur (#8)
Legg til fastlane/metadata/android/ med tittel, skildringar og
endringsloggar for en-US, nb-NO og nn-NO (versionCode 3–6).
Dokumenter Play Services-avhengigheit og anti-feature-vurdering.

Skjermbilete må framleis leggjast til i images/-mappene.

Closes #8

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:07:01 +01:00
3c1da8adec Legg til haptisk tilbakemelding på knappetrykk og listeval (#11)
Android: HapticFeedbackConstants.VIRTUAL_KEY på alle knappar og
listeelement. PWA: navigator.vibrate(10ms) på same interaksjonar.

Closes #11

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:01:58 +01:00
c35ccb91bf Mjuk opp tonen i sivilforsvarsdialog-tittelen
Endrar «når alarmen går» til «hvis/om alarmen går» for å unngå
å implisere at ein nødsituasjon faktisk skjer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:57:58 +01:00
d4364379ab Legg til opphavsrettsmelding
Legg til copyright-notis i README og i sivilforsvarsdialogen i appen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:54:06 +01:00
7ca06948f1 Gjer widget-tidsstempel tydelegare med «Oppdatert»-prefiks
Endrar widget-tidsstempel frå berre «14:32» til «Oppdatert 14:32»
slik at det er tydeleg at dette er siste oppdateringstidspunkt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:53:43 +01:00
1a72314e30 Legg til tilgjengelegheitsetiketter i PWA
- Legg til role=img og aria-label på retningspil-canvas
- Oppdater aria-label dynamisk med avstand ved val av tilfluktsrom
- Legg til direction_arrow_description-streng i alle tre språk

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:53:17 +01:00
fa4c379176 Forbetr kontrast på statustekst for WCAG AA
Endrar status_text frå #B0BEC5 (~4.5:1) til #CFD8DC (~6.5:1) mot
#16213E-bakgrunnen, i både Android-appen og PWA-en.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:52:54 +01:00
a3728798b8 Legg til tilgjengelegheitsetiketter og skjermlesar-fiksar (Android)
- Legg til contentDescription på tilfluktsrom-listeelement for TalkBack
- Erstatt emoji-kompassåtvaring med tilgjengeleg tekstresurs
- Vis «Ventar på GPS…» i staden for «—» ved ukjend avstand
- Legg til content_desc_shelter_item og compass_accuracy_warning-strengar (en/nb/nn)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:52:39 +01:00
5fd76678d4 Legg til sentralisert datakildeoversikt (SOURCES.md)
Samlar all attribusjon og lisensinformasjon for eksterne datakjelder
(Geonorge, OpenStreetMap, DSB) i éi fil. Førebur for issue #9
(internasjonale datakjelder).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:41:48 +01:00
47 changed files with 258 additions and 22 deletions

44
F-DROID.md Normal file
View file

@ -0,0 +1,44 @@
# F-Droid submission notes
## Anti-features: Play Services dependency
The app includes `com.google.android.gms:play-services-location:21.3.0` for better location accuracy via `FusedLocationProviderClient`. However, this is **not a hard dependency**:
- The app checks for Play Services at runtime via `GoogleApiAvailability`
- If unavailable, it falls back to `LocationManager` (standard AOSP API)
- All core functionality (finding shelters, compass navigation, offline maps) works without Play Services
This was specifically designed to support degoogled devices (LineageOS, GrapheneOS, /e/OS).
### F-Droid build options
**Option A: Accept as-is with `NonFreeDep` anti-feature**
The app works fully without Play Services. Mark with `NonFreeDep` anti-feature.
**Option B: Build flavor without Play Services (recommended)**
Create a `fdroid` product flavor that excludes the Play Services dependency entirely. The fallback code paths already handle the absence — only the dependency and the Fused provider code need to be conditionally included.
## Metadata structure
```
fastlane/metadata/android/
├── en-US/ # English (default)
├── nb-NO/ # Norwegian Bokmål
└── nn-NO/ # Norwegian Nynorsk
```
Each locale contains `title.txt`, `short_description.txt`, `full_description.txt`, and `changelogs/` with per-versionCode files.
## Screenshots
Screenshots still need to be added to `images/` directories:
- `phoneScreenshots/` — at least 3 phone screenshots
- `featureGraphic.png` — 1024x500 feature graphic
## Build instructions
Standard Gradle build, no custom steps needed:
```
./gradlew assembleRelease
```

View file

@ -77,9 +77,12 @@ Appen er designet etter «offline-first»-prinsippet:
- Databaseoppdateringer er atomiske (transaksjon) for å unngå datatap
- Ingen persondata lagres — kun tilfluktsromdata og kartfliser
## Opphavsrett
Copyright (c) Ole-Morten Duesund <olemd@glemt.net>
## Lisens
Kildekoden er lisensiert under [Mozilla Public License 2.0](LICENSE).
Tilfluktsromdata er åpne data fra Geonorge / Direktoratet for samfunnssikkerhet og beredskap (DSB).
Kartfliser fra OpenStreetMap er lisensiert under [ODbL](https://opendatacommons.org/licenses/odbl/).
Appen bruker åpne data og tjenester fra flere kilder. Se [SOURCES.md](SOURCES.md) for en fullstendig oversikt over datakilder, URL-er og lisenser.

17
SOURCES.md Normal file
View file

@ -0,0 +1,17 @@
# Data Sources
Centralized list of all external data sources used by the app. This is the authoritative reference for attribution, licensing, and usage context.
## Sources
| Source | Provider | URL | Usage | License | Used in |
|--------|----------|-----|-------|---------|---------|
| Shelter data (GeoJSON) | Geonorge / DSB | [nedlasting.geonorge.no/...](https://nedlasting.geonorge.no/geonorge/Samfunnssikkerhet/TilfluktsromOffentlige/GeoJSON/Samfunnssikkerhet_0000_Norge_25833_TilfluktsromOffentlige_GeoJSON.zip) | Shelter locations, capacity, addresses (~556 shelters) | [NLOD 2.0](https://data.norge.no/nlod/no/2.0) | `app/.../data/ShelterRepository.kt`, `pwa/scripts/fetch-shelters.ts` |
| Map tiles | OpenStreetMap | [tile.openstreetmap.org](https://tile.openstreetmap.org) | Offline-capable map display | [ODbL](https://opendatacommons.org/licenses/odbl/) | `app/` via OSMDroid, `pwa/src/ui/map-view.ts` via Leaflet |
| Civil defense guidelines | DSB (Direktoratet for samfunnssikkerhet og beredskap) | [dsb.no/sikkerhverdag/egenberedskap](https://www.dsb.no/sikkerhverdag/egenberedskap/) | Emergency instructions shown in the civil defense info dialog | Norwegian public sector information | `app/.../res/values/strings.xml` (civil_defense_* strings) |
## Notes
- **Shelter data** is downloaded as a ZIP containing GeoJSON in EPSG:25833 (UTM33N) projection. The app converts coordinates to WGS84 at parse time.
- **Map tiles** are cached locally by OSMDroid (Android) and the service worker (PWA) for offline use. OpenStreetMap's [tile usage policy](https://operations.osmfoundation.org/policies/tiles/) applies.
- **Civil defense guidelines** are adapted from official DSB recommendations, not quoted verbatim. Content is available in English, Bokmal, and Nynorsk.

View file

@ -3,6 +3,7 @@ package no.naiv.tilfluktsrom
import android.Manifest
import android.content.Context
import android.content.Intent
import android.view.HapticFeedbackConstants
import android.content.pm.PackageManager
import android.hardware.Sensor
import android.hardware.SensorEvent
@ -202,6 +203,7 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
private fun setupButtons() {
binding.toggleViewFab.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
isCompassMode = !isCompassMode
if (isCompassMode) {
binding.mapView.visibility = View.GONE
@ -216,24 +218,29 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
// Reset to navigation: re-fit map to show user + selected shelter
binding.resetNavigationFab.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
userHasInteractedWithMap = false
binding.resetNavigationFab.visibility = View.GONE
selectedShelter?.let { highlightShelterOnMap(it) }
}
binding.infoButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
CivilDefenseInfoDialog().show(supportFragmentManager, CivilDefenseInfoDialog.TAG)
}
binding.refreshButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
forceRefresh()
}
binding.shareButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
shareShelter()
}
binding.cacheRetryButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
val loc = currentLocation
if (loc == null) {
Toast.makeText(this, R.string.status_no_location, Toast.LENGTH_SHORT).show()
@ -489,7 +496,11 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
/** Update all UI elements for the currently selected shelter. */
private fun updateSelectedShelterUI() {
val selected = selectedShelter ?: return
val distanceText = DistanceUtils.formatDistance(selected.distanceMeters)
val distanceText = if (selected.distanceMeters.isNaN()) {
getString(R.string.status_no_location)
} else {
DistanceUtils.formatDistance(selected.distanceMeters)
}
// Update bottom sheet
binding.selectedShelterAddress.text = selected.shelter.adresse
@ -829,9 +840,7 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
Log.w(TAG, "Compass accuracy degraded: $accuracy")
binding.compassAddressText.let { tv ->
val current = selectedShelter?.shelter?.adresse ?: ""
if (!current.contains("")) {
tv.text = "$current"
}
tv.text = getString(R.string.compass_accuracy_warning, current)
}
}
SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM,

View file

@ -1,5 +1,6 @@
package no.naiv.tilfluktsrom.ui
import android.view.HapticFeedbackConstants
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
@ -52,10 +53,18 @@ class ShelterListAdapter(
R.string.shelter_room_nr, item.shelter.romnr
)
binding.root.contentDescription = ctx.getString(
R.string.content_desc_shelter_item,
item.shelter.adresse,
DistanceUtils.formatDistance(item.distanceMeters),
item.shelter.plasser
)
binding.root.isSelected = isSelected
binding.root.alpha = if (isSelected) 1.0f else 0.7f
binding.root.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
val pos = adapterPosition
if (pos != RecyclerView.NO_POSITION) {
selectPosition(pos)

View file

@ -221,10 +221,11 @@ class ShelterWidgetProvider : AppWidgetProvider() {
views.setTextViewText(R.id.widgetTimestamp, formatTimestamp(context))
}
/** Format current time as HH:mm, respecting the user's 12/24h preference. */
/** Format current time as "Updated HH:mm", respecting the user's 12/24h preference. */
private fun formatTimestamp(context: Context): String {
val format = DateFormat.getTimeFormat(context)
return format.format(System.currentTimeMillis())
val timeStr = format.format(System.currentTimeMillis())
return context.getString(R.string.widget_updated_at, timeStr)
}
/**

View file

@ -114,5 +114,14 @@
android:textSize="12sp"
android:textStyle="italic" />
<!-- Copyright notice -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/app_copyright"
android:textColor="@color/text_secondary"
android:textSize="11sp" />
</LinearLayout>
</ScrollView>

View file

@ -51,6 +51,7 @@
<string name="widget_open_app">\u00c5pne appen for posisjon</string>
<string name="widget_no_data">Ingen tilfluktsromdata</string>
<string name="widget_no_location">Trykk for \u00e5 oppdatere</string>
<string name="widget_updated_at">Oppdatert %s</string>
<!-- Dataferskhet -->
<string name="freshness_fresh">Data er oppdatert</string>
@ -64,10 +65,12 @@
<!-- Tilgjengelighet -->
<string name="direction_arrow_description">Retning til tilfluktsrom, %s unna</string>
<string name="content_desc_shelter_item">%1$s, %2$s, %3$d plasser</string>
<string name="compass_accuracy_warning">Upresist kompass - %s</string>
<!-- Sivilforsvar -->
<string name="action_civil_defense_info">Sivilforsvarsinformasjon</string>
<string name="civil_defense_title">Hva du skal gjøre når alarmen går</string>
<string name="civil_defense_title">Hva du skal gjøre hvis alarmen går</string>
<string name="civil_defense_step1_title">1. Viktig melding-signalet</string>
<string name="civil_defense_step1_body">Tre serier med korte støt med ett minutts stillhet mellom hver serie. Dette betyr: søk informasjon umiddelbart. Slå på DAB-radio, TV, eller sjekk offisielle kilder på nett.</string>
<string name="civil_defense_step2_title">2. Flyalarm</string>
@ -79,4 +82,7 @@
<string name="civil_defense_step5_title">5. Faren over</string>
<string name="civil_defense_step5_body">Én sammenhengende tone på omtrent 30 sekunder. Faren eller angrepet er over. Fortsett å følge instruksjoner fra myndighetene.</string>
<string name="civil_defense_source">Kilde: DSB (Direktoratet for samfunnssikkerhet og beredskap)</string>
<!-- Opphavsrett -->
<string name="app_copyright">Opphavsrett © Ole-Morten Duesund</string>
</resources>

View file

@ -51,6 +51,7 @@
<string name="widget_open_app">Opne appen for posisjon</string>
<string name="widget_no_data">Ingen tilfluktsromdata</string>
<string name="widget_no_location">Trykk for \u00e5 oppdatere</string>
<string name="widget_updated_at">Oppdatert %s</string>
<!-- Dataferskheit -->
<string name="freshness_fresh">Data er oppdatert</string>
@ -64,10 +65,12 @@
<!-- Tilgjenge -->
<string name="direction_arrow_description">Retning til tilfluktsrom, %s unna</string>
<string name="content_desc_shelter_item">%1$s, %2$s, %3$d plassar</string>
<string name="compass_accuracy_warning">Upresis kompass - %s</string>
<!-- Sivilforsvar -->
<string name="action_civil_defense_info">Sivilforsvarsinformasjon</string>
<string name="civil_defense_title">Kva du skal gjere når alarmen går</string>
<string name="civil_defense_title">Kva du skal gjere om alarmen går</string>
<string name="civil_defense_step1_title">1. Viktig melding-signalet</string>
<string name="civil_defense_step1_body">Tre seriar med korte støyt med eitt minutt stille mellom kvar serie. Dette tyder: søk informasjon med ein gong. Slå på DAB-radio, TV, eller sjekk offisielle kjelder på nett.</string>
<string name="civil_defense_step2_title">2. Flyalarm</string>
@ -79,4 +82,7 @@
<string name="civil_defense_step5_title">5. Faren over</string>
<string name="civil_defense_step5_body">Éin samanhengande tone på omtrent 30 sekund. Faren eller åtaket er over. Hald fram med å følgje instruksjonar frå styresmaktene.</string>
<string name="civil_defense_source">Kjelde: DSB (Direktoratet for samfunnstryggleik og beredskap)</string>
<!-- Opphavsrett -->
<string name="app_copyright">Opphavsrett © Ole-Morten Duesund</string>
</resources>

View file

@ -7,7 +7,7 @@
<color name="background">#1A1A2E</color>
<color name="status_bar_bg">#16213E</color>
<color name="status_text">#B0BEC5</color>
<color name="status_text">#CFD8DC</color>
<color name="bottom_sheet_bg">#1A1A2E</color>
<color name="compass_bg">#0F0F23</color>
<color name="loading_bg">#CC000000</color>

View file

@ -51,6 +51,7 @@
<string name="widget_open_app">Open app for location</string>
<string name="widget_no_data">No shelter data</string>
<string name="widget_no_location">Tap to refresh</string>
<string name="widget_updated_at">Updated %s</string>
<!-- Data freshness -->
<string name="freshness_fresh">Data is up to date</string>
@ -64,10 +65,12 @@
<!-- Accessibility -->
<string name="direction_arrow_description">Direction to shelter, %s away</string>
<string name="content_desc_shelter_item">%1$s, %2$s, %3$d places</string>
<string name="compass_accuracy_warning">Low accuracy - %s</string>
<!-- Civil defense info -->
<string name="action_civil_defense_info">Civil defense information</string>
<string name="civil_defense_title">What to do when the alarm sounds</string>
<string name="civil_defense_title">What to do if the alarm sounds</string>
<string name="civil_defense_step1_title">1. Important message signal</string>
<string name="civil_defense_step1_body">Three series of short blasts with one minute of silence between each series. This means: seek information immediately. Turn on DAB radio, TV, or check official sources online.</string>
<string name="civil_defense_step2_title">2. Air raid alarm</string>
@ -79,4 +82,7 @@
<string name="civil_defense_step5_title">5. All clear</string>
<string name="civil_defense_step5_body">One continuous tone lasting approximately 30 seconds. The danger or attack is over. Continue to follow instructions from authorities.</string>
<string name="civil_defense_source">Source: DSB (Norwegian Directorate for Civil Protection)</string>
<!-- Copyright -->
<string name="app_copyright">Copyright © Ole-Morten Duesund</string>
</resources>

View file

@ -0,0 +1,4 @@
• Hybrid location: uses Play Services when available, falls back to standard Android GPS
• Data freshness indicator shows when shelter data was last updated
• Home screen widget showing nearest shelter with distance
• Bundled shelter data for instant offline use on first launch

View file

@ -0,0 +1,3 @@
• Share shelter location with others via any app
• Deep link support — open shared shelters directly in the app
• Tap any marker on the map to select and navigate to it

View file

@ -0,0 +1,2 @@
• Widget now updates automatically every 15 minutes via WorkManager
• Fixed widget not showing data without opening the app first

View file

@ -0,0 +1,4 @@
• Civil defense information: what to do if the alarm sounds (based on DSB guidelines)
• Improved accessibility: screen reader labels, better contrast, haptic feedback
• Widget shows "Updated 14:32" instead of bare timestamp
• Copyright notice added

View file

@ -0,0 +1,6 @@
• Accessibility: screen reader labels on shelter list, compass, and direction arrows
• Improved contrast on status bar text (WCAG AA)
• Haptic feedback on all buttons and list items
• Widget timestamp now shows "Updated 14:32"
• Softer civil defense dialog title
• Copyright notice added

View file

@ -0,0 +1,15 @@
Find the nearest public emergency shelter (tilfluktsrom) in Norway. Built for emergencies — works fully offline after first use.
Features:
• Shows the 3 nearest shelters with distance and capacity
• Compass navigation — direction arrow points to the selected shelter
• Offline map — map tiles are cached automatically for use without internet
• Select any shelter — tap any marker on the map to navigate there
• Home screen widget — shows nearest shelter at a glance
• Share shelters — send shelter location to others via any app
• Civil defense info — what to do if the alarm sounds
• Multilingual — English, Bokmål, and Nynorsk
The app uses open data from Geonorge (Norwegian Mapping Authority) covering approximately 556 public shelters across Norway.
Works on degoogled devices: the app uses Google Play Services for better location accuracy when available, but falls back to standard Android location APIs on devices without Play Services (LineageOS, GrapheneOS, /e/OS, etc.).

View file

@ -0,0 +1 @@
Find the nearest public emergency shelter in Norway. Works offline.

View file

@ -0,0 +1 @@
Tilfluktsrom

View file

@ -0,0 +1,4 @@
• Hybrid posisjon: bruker Play Services når tilgjengelig, faller tilbake til standard Android-GPS
• Dataferskhetsindikator viser når tilfluktsromdata sist ble oppdatert
• Hjemskjerm-widget som viser nærmeste tilfluktsrom med avstand
• Medfølgende tilfluktsromdata for umiddelbar frakoblet bruk ved første oppstart

View file

@ -0,0 +1,3 @@
• Del tilfluktsrom med andre via en hvilken som helst app
• Støtte for dyplenker — åpne delte tilfluktsrom direkte i appen
• Trykk på en markør i kartet for å velge og navigere dit

View file

@ -0,0 +1,2 @@
• Widget oppdateres nå automatisk hvert 15. minutt via WorkManager
• Fikset at widget ikke viste data uten å åpne appen først

View file

@ -0,0 +1,4 @@
• Sivilforsvarsinformasjon: hva du skal gjøre hvis alarmen går (basert på DSB-retningslinjer)
• Forbedret tilgjengelighet: skjermleseretiketter, bedre kontrast, haptisk tilbakemelding
• Widget viser «Oppdatert 14:32» i stedet for bare tidsstempel
• Opphavsrettsmelding lagt til

View file

@ -0,0 +1,6 @@
• Tilgjengelighet: skjermleseretiketter på tilfluktsromliste, kompass og retningspil
• Forbedret kontrast på statuslinjetekst (WCAG AA)
• Haptisk tilbakemelding på alle knapper og listeelement
• Widget-tidsstempel viser nå «Oppdatert 14:32»
• Mildere tittel på sivilforsvarsdialog
• Opphavsrettsmelding lagt til

View file

@ -0,0 +1,15 @@
Finn nærmeste offentlige tilfluktsrom i Norge. Bygd for nødsituasjoner — fungerer helt uten internett etter første gangs bruk.
Funksjoner:
• Viser de 3 nærmeste tilfluktsrommene med avstand og kapasitet
• Kompassnavigasjon — retningspil som peker mot valgt tilfluktsrom
• Frakoblet kart — kartfliser lagres automatisk for bruk uten nett
• Velg fritt — trykk på en markør i kartet for å navigere dit
• Hjemskjerm-widget — viser nærmeste tilfluktsrom med ett blikk
• Del tilfluktsrom — send posisjon til andre via en hvilken som helst app
• Sivilforsvarsinformasjon — hva du skal gjøre hvis alarmen går
• Flerspråklig — engelsk, bokmål og nynorsk
Appen bruker åpne data fra Geonorge (Kartverket) med ca. 556 offentlige tilfluktsrom i hele Norge.
Fungerer på de-Google-enheter: appen bruker Google Play Services for bedre posisjonsdata når det er tilgjengelig, men faller tilbake til standard Android-posisjons-API-er på enheter uten Play Services (LineageOS, GrapheneOS, /e/OS osv.).

View file

@ -0,0 +1 @@
Finn nærmeste offentlige tilfluktsrom i Norge. Fungerer uten nett.

View file

@ -0,0 +1 @@
Tilfluktsrom

View file

@ -0,0 +1,4 @@
• Hybrid posisjon: brukar Play Services når tilgjengeleg, fell tilbake til standard Android-GPS
• Dataferskheitsindikator viser når tilfluktsromdata sist vart oppdatert
• Heimeskjerm-widget som viser næraste tilfluktsrom med avstand
• Medfølgjande tilfluktsromdata for umiddelbar fråkopla bruk ved fyrste oppstart

View file

@ -0,0 +1,3 @@
• Del tilfluktsrom med andre via ei kva som helst app
• Støtte for djuplenker — opne delte tilfluktsrom direkte i appen
• Trykk på ein markør i kartet for å velje og navigere dit

View file

@ -0,0 +1,2 @@
• Widget oppdaterast no automatisk kvart 15. minutt via WorkManager
• Fiksa at widget ikkje viste data utan å opne appen fyrst

View file

@ -0,0 +1,4 @@
• Sivilforsvarsinformasjon: kva du skal gjere om alarmen går (basert på DSB-retningslinjer)
• Forbetra tilgjenge: skjermlesar-etiketter, betre kontrast, haptisk tilbakemelding
• Widget viser «Oppdatert 14:32» i staden for berre tidsstempel
• Opphavsrettsmelding lagt til

View file

@ -0,0 +1,6 @@
• Tilgjenge: skjermlesar-etiketter på tilfluktsromliste, kompass og retningspil
• Forbetra kontrast på statuslinjetekst (WCAG AA)
• Haptisk tilbakemelding på alle knappar og listeelement
• Widget-tidsstempel viser no «Oppdatert 14:32»
• Mildare tittel på sivilforsvarsdialog
• Opphavsrettsmelding lagt til

View file

@ -0,0 +1,15 @@
Finn næraste offentlege tilfluktsrom i Noreg. Bygd for nødsituasjonar — fungerer heilt utan internett etter fyrste gongs bruk.
Funksjonar:
• Viser dei 3 næraste tilfluktsromma med avstand og kapasitet
• Kompassnavigasjon — retningspil som peikar mot valt tilfluktsrom
• Fråkopla kart — kartfliser lagrast automatisk for bruk utan nett
• Vel fritt — trykk på ein markør i kartet for å navigere dit
• Heimeskjerm-widget — viser næraste tilfluktsrom med eitt blikk
• Del tilfluktsrom — send posisjon til andre via ei kva som helst app
• Sivilforsvarsinformasjon — kva du skal gjere om alarmen går
• Fleirspråkleg — engelsk, bokmål og nynorsk
Appen brukar opne data frå Geonorge (Kartverket) med ca. 556 offentlege tilfluktsrom i heile Noreg.
Fungerer på de-Google-einingar: appen brukar Google Play Services for betre posisjonsdata når det er tilgjengeleg, men fell tilbake til standard Android-posisjons-API-ar på einingar utan Play Services (LineageOS, GrapheneOS, /e/OS osb.).

View file

@ -0,0 +1 @@
Finn næraste offentlege tilfluktsrom i Noreg. Fungerer utan nett.

View file

@ -0,0 +1 @@
Tilfluktsrom

View file

@ -43,7 +43,7 @@
<!-- Bottom sheet with shelter info -->
<div id="bottom-sheet">
<div id="selected-shelter">
<canvas id="mini-arrow" width="96" height="96"></canvas>
<canvas id="mini-arrow" width="96" height="96" role="img" aria-label="Direction to shelter"></canvas>
<div id="selected-shelter-info">
<div id="selected-shelter-address"></div>
<div id="selected-shelter-details"></div>

View file

@ -76,6 +76,7 @@ function setupButtons(): void {
// Toggle map/compass
const toggleFab = document.getElementById('toggle-fab')!;
toggleFab.addEventListener('click', async () => {
navigator.vibrate?.(10);
isCompassMode = !isCompassMode;
const mapContainer = document.getElementById('map-container')!;
@ -112,6 +113,7 @@ function setupButtons(): void {
const cacheRetryBtn = document.getElementById('cache-retry-btn')!;
cacheRetryBtn.textContent = t('action_cache_now');
cacheRetryBtn.addEventListener('click', () => {
navigator.vibrate?.(10);
if (currentLocation && navigator.onLine) {
startCaching(currentLocation.latitude, currentLocation.longitude);
}
@ -120,6 +122,7 @@ function setupButtons(): void {
// Reset view button
const resetBtn = document.getElementById('reset-view-btn')!;
resetBtn.addEventListener('click', () => {
navigator.vibrate?.(10);
const selected = nearestShelters[selectedShelterIndex] ?? null;
mapView.resetView(selected, currentLocation);
resetBtn.classList.remove('visible');
@ -256,6 +259,8 @@ function updateSelectedShelter(isUserAction: boolean): void {
].join(' \u00B7 ');
// Update mini arrow
const miniArrow = document.getElementById('mini-arrow')!;
miniArrow.setAttribute('aria-label', t('direction_arrow_description', dist));
updateMiniArrow(selected.bearingDegrees - deviceHeading);
// Update compass view

View file

@ -46,4 +46,7 @@ export const en: Record<string, string> = {
'No cached data available. Connect to the internet to download shelter data.',
update_success: 'Shelter data updated',
update_failed: 'Update failed \u2014 using cached data',
// Accessibility
direction_arrow_description: 'Direction to shelter, %s away',
};

View file

@ -41,4 +41,7 @@ export const nb: Record<string, string> = {
'Ingen lagrede data tilgjengelig. Koble til internett for \u00e5 laste ned tilfluktsromdata.',
update_success: 'Tilfluktsromdata oppdatert',
update_failed: 'Oppdatering mislyktes \u2014 bruker lagrede data',
// Tilgjengelighet
direction_arrow_description: 'Retning til tilfluktsrom, %s unna',
};

View file

@ -41,4 +41,7 @@ export const nn: Record<string, string> = {
'Ingen lagra data tilgjengeleg. Kopla til internett for \u00e5 laste ned tilfluktsromdata.',
update_success: 'Tilfluktsromdata oppdatert',
update_failed: 'Oppdatering mislukkast \u2014 brukar lagra data',
// Tilgjenge
direction_arrow_description: 'Retning til tilfluktsrom, %s unna',
};

View file

@ -40,7 +40,7 @@ html, body {
#status-text {
flex: 1;
color: #B0BEC5;
color: #CFD8DC;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
@ -50,7 +50,7 @@ html, body {
#refresh-btn {
background: none;
border: none;
color: #B0BEC5;
color: #CFD8DC;
cursor: pointer;
padding: 4px;
font-size: 18px;
@ -142,8 +142,8 @@ html, body {
height: 40px;
border-radius: 50%;
background: #16213E;
border: 2px solid #B0BEC5;
color: #B0BEC5;
border: 2px solid #CFD8DC;
color: #CFD8DC;
font-size: 18px;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
@ -356,7 +356,7 @@ html, body {
}
.leaflet-control-attribution a {
color: #B0BEC5 !important;
color: #CFD8DC !important;
}
.leaflet-popup-content-wrapper {
@ -370,5 +370,5 @@ html, body {
}
.leaflet-popup-close-button {
color: #B0BEC5 !important;
color: #CFD8DC !important;
}

View file

@ -52,6 +52,7 @@ export function updateList(
item.appendChild(addressSpan);
item.appendChild(detailsSpan);
item.addEventListener('click', () => {
navigator.vibrate?.(10);
onSelect?.(i);
});
container!.appendChild(item);

View file

@ -11,5 +11,8 @@ export function setStatus(text: string): void {
/** Set the refresh button click handler. */
export function onRefreshClick(handler: () => void): void {
const btn = document.getElementById('refresh-btn');
if (btn) btn.addEventListener('click', handler);
if (btn) btn.addEventListener('click', () => {
navigator.vibrate?.(10);
handler();
});
}

View file

@ -1,4 +1,4 @@
versionMajor=1
versionMinor=4
versionPatch=0
versionCode=6
versionPatch=1
versionCode=7