diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c544235..073fa52 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -215,16 +215,9 @@ Both flavors produce identical user experiences — `standard` achieves faster G ### Deep Linking -**HTTPS App Links:** `https://tilfluktsrom.naiv.no/shelter/{lokalId}` +URI scheme: `tilfluktsrom://shelter/{lokalId}` -The domain is configured in one place: `DEEP_LINK_DOMAIN` in `build.gradle.kts` (exposed as `BuildConfig.DEEP_LINK_DOMAIN` and manifest placeholder `${deepLinkHost}`). - -- `autoVerify="true"` on the HTTPS intent filter triggers Android's App Links verification at install time -- Verification requires `/.well-known/assetlinks.json` to be served by the PWA (in `pwa/public/.well-known/`) -- If the app is installed and verified, `/shelter/*` links open the app directly (no disambiguation dialog) -- If not installed, the link opens in the browser, where the PWA handles it - -Share messages include the HTTPS URL, which SMS apps auto-link as a tappable URL. +Used in share messages so recipients can open the app directly to a specific shelter. --- diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5992c73..c46b0b4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,13 +14,8 @@ android { applicationId = "no.naiv.tilfluktsrom" minSdk = 26 targetSdk = 35 - versionCode = 12 - versionName = "1.8.0" - - // Deep link domain — single source of truth for manifest + Kotlin code - val deepLinkDomain = "tilfluktsrom.naiv.no" - buildConfigField("String", "DEEP_LINK_DOMAIN", "\"$deepLinkDomain\"") - manifestPlaceholders["deepLinkHost"] = deepLinkDomain + versionCode = 11 + versionName = "1.7.0" } dependenciesInfo { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e522483..58bf2ed 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,14 +29,13 @@ - + + android:scheme="tilfluktsrom" + android:host="shelter" /> diff --git a/app/src/main/java/no/naiv/tilfluktsrom/MainActivity.kt b/app/src/main/java/no/naiv/tilfluktsrom/MainActivity.kt index 5f80847..e1fd8e0 100644 --- a/app/src/main/java/no/naiv/tilfluktsrom/MainActivity.kt +++ b/app/src/main/java/no/naiv/tilfluktsrom/MainActivity.kt @@ -143,14 +143,12 @@ class MainActivity : AppCompatActivity(), SensorEventListener { } /** - * Handle https://{domain}/shelter/{lokalId} deep link. + * Handle tilfluktsrom://shelter/{lokalId} deep link. * If shelters are already loaded, select immediately; otherwise store as pending. */ private fun handleDeepLinkIntent(intent: Intent?) { val uri = intent?.data ?: return - if (uri.scheme != "https" || - uri.host != BuildConfig.DEEP_LINK_DOMAIN || - uri.path?.startsWith("/shelter/") != true) return + if (uri.scheme != "tilfluktsrom" || uri.host != "shelter") return val lokalId = uri.lastPathSegment ?: return // Clear intent data so config changes don't re-trigger @@ -671,8 +669,8 @@ class MainActivity : AppCompatActivity(), SensorEventListener { /** * Share the currently selected shelter via ACTION_SEND. - * Includes address, capacity, geo: URI, and an HTTPS deep link - * that opens the app (if installed) or the PWA (in browser). + * Includes address, capacity, geo: URI (for non-app recipients), + * and a tilfluktsrom:// deep link (for app users). */ private fun shareShelter() { val selected = selectedShelter @@ -682,14 +680,12 @@ class MainActivity : AppCompatActivity(), SensorEventListener { } val shelter = selected.shelter - val deepLink = "https://${BuildConfig.DEEP_LINK_DOMAIN}/shelter/${shelter.lokalId}" val body = getString( R.string.share_body, shelter.adresse, shelter.plasser, shelter.latitude, - shelter.longitude, - deepLink + shelter.longitude ) val shareIntent = Intent(Intent.ACTION_SEND).apply { diff --git a/app/src/main/java/no/naiv/tilfluktsrom/data/ShelterRepository.kt b/app/src/main/java/no/naiv/tilfluktsrom/data/ShelterRepository.kt index 048a93d..b5afd3b 100644 --- a/app/src/main/java/no/naiv/tilfluktsrom/data/ShelterRepository.kt +++ b/app/src/main/java/no/naiv/tilfluktsrom/data/ShelterRepository.kt @@ -46,7 +46,7 @@ class ShelterRepository(private val context: Context) { .readTimeout(60, TimeUnit.SECONDS) .addInterceptor(Interceptor { chain -> chain.proceed(chain.request().newBuilder() - .header("User-Agent", "Tilfluktsrom/1.8.0") + .header("User-Agent", "Tilfluktsrom/1.7.0") .build()) }) .build() diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 8f0f2d3..74c464b 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -60,7 +60,7 @@ Tilfluktsrom - Tilfluktsrom: %1$s\n%2$d plasser\n%3$.6f, %4$.6f\ngeo:%3$.6f,%4$.6f\n%5$s + Tilfluktsrom: %1$s\n%2$d plasser\n%3$.6f, %4$.6f\ngeo:%3$.6f,%4$.6f Ingen tilfluktsrom valgt diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index 088398a..f3077e5 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -60,7 +60,7 @@ Tilfluktsrom - Tilfluktsrom: %1$s\n%2$d plassar\n%3$.6f, %4$.6f\ngeo:%3$.6f,%4$.6f\n%5$s + Tilfluktsrom: %1$s\n%2$d plassar\n%3$.6f, %4$.6f\ngeo:%3$.6f,%4$.6f Ingen tilfluktsrom valt diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34afc98..38d09b2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,7 +60,7 @@ Emergency shelter - Shelter: %1$s\n%2$d places\n%3$.6f, %4$.6f\ngeo:%3$.6f,%4$.6f\n%5$s + Shelter: %1$s\n%2$d places\n%3$.6f, %4$.6f\ngeo:%3$.6f,%4$.6f No shelter selected diff --git a/pwa/public/.well-known/assetlinks.json b/pwa/public/.well-known/assetlinks.json deleted file mode 100644 index b95a5f1..0000000 --- a/pwa/public/.well-known/assetlinks.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "relation": ["delegate_permission/common.handle_all_urls"], - "target": { - "namespace": "android_app", - "package_name": "no.naiv.tilfluktsrom", - "sha256_cert_fingerprints": [ - "43:05:79:6F:EA:3E:F4:50:45:D3:8A:EF:EA:58:B6:65:49:D2:D2:C3:4B:4C:61:11:EE:74:48:B0:C7:70:E4:5B" - ] - } - } -] diff --git a/pwa/src/app.ts b/pwa/src/app.ts index ceceb58..f2bee0a 100644 --- a/pwa/src/app.ts +++ b/pwa/src/app.ts @@ -9,7 +9,7 @@ import type { Shelter, ShelterWithDistance, LatLon } from './types'; import { t } from './i18n/i18n'; -import { formatDistance, distanceMeters, bearingDegrees } from './util/distance-utils'; +import { formatDistance } from './util/distance-utils'; import { findNearest } from './location/shelter-finder'; import * as repo from './data/shelter-repository'; import * as locationProvider from './location/location-provider'; @@ -43,7 +43,6 @@ export async function init(): Promise { setupShelterList(); setupButtons(); await loadData(); - handleDeepLink(); } /** Set localized aria-labels and wire the about button. */ @@ -399,67 +398,3 @@ async function forceRefresh(): Promise { statusBar.setStatus(t('update_failed')); } } - -/** - * Handle /shelter/{lokalId} deep links. - * Called after loadData() so allShelters is populated. - */ -function handleDeepLink(): void { - const match = window.location.pathname.match(/^\/shelter\/(.+)$/); - if (!match) return; - - const lokalId = decodeURIComponent(match[1]); - - // Clean the URL so refresh doesn't re-trigger - window.history.replaceState({}, '', '/'); - - const shelter = allShelters.find((s) => s.lokalId === lokalId); - if (!shelter) { - statusBar.setStatus(t('error_shelter_not_found')); - return; - } - - selectShelterByData(shelter); -} - -/** - * Select a specific shelter, even if it's not in the current nearest-3 list. - * Used for deep link targets. - */ -function selectShelterByData(shelter: Shelter): void { - // Check if it's already in nearestShelters - const existingIdx = nearestShelters.findIndex( - (s) => s.shelter.lokalId === shelter.lokalId, - ); - - if (existingIdx >= 0) { - userSelectedShelter = true; - selectedShelterIndex = existingIdx; - } else { - // Compute distance/bearing if we have a location, otherwise use placeholder - let dist = NaN; - let bearing = 0; - if (currentLocation) { - dist = distanceMeters( - currentLocation.latitude, currentLocation.longitude, - shelter.latitude, shelter.longitude, - ); - bearing = bearingDegrees( - currentLocation.latitude, currentLocation.longitude, - shelter.latitude, shelter.longitude, - ); - } - - // Prepend to the list so it becomes the selected shelter - nearestShelters.unshift({ - shelter, - distanceMeters: dist, - bearingDegrees: bearing, - }); - userSelectedShelter = true; - selectedShelterIndex = 0; - shelterList.updateList(nearestShelters, selectedShelterIndex); - } - - updateSelectedShelter(true); -} diff --git a/pwa/src/config.ts b/pwa/src/config.ts deleted file mode 100644 index a0ccb93..0000000 --- a/pwa/src/config.ts +++ /dev/null @@ -1,2 +0,0 @@ -/** Deep link domain — single source of truth for the PWA. */ -export const DEEP_LINK_DOMAIN = 'tilfluktsrom.naiv.no'; diff --git a/pwa/src/i18n/en.ts b/pwa/src/i18n/en.ts index 6a77154..1630463 100644 --- a/pwa/src/i18n/en.ts +++ b/pwa/src/i18n/en.ts @@ -46,7 +46,6 @@ export const en: Record = { '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', - error_shelter_not_found: 'Shelter not found', // Accessibility direction_arrow_description: 'Direction to shelter, %s away', diff --git a/pwa/src/i18n/nb.ts b/pwa/src/i18n/nb.ts index 8c285d8..2ccca12 100644 --- a/pwa/src/i18n/nb.ts +++ b/pwa/src/i18n/nb.ts @@ -41,7 +41,6 @@ export const nb: Record = { 'Ingen lagrede data tilgjengelig. Koble til internett for å laste ned tilfluktsromdata.', update_success: 'Tilfluktsromdata oppdatert', update_failed: 'Oppdatering mislyktes — bruker lagrede data', - error_shelter_not_found: 'Fant ikke tilfluktsrommet', // Tilgjengelighet direction_arrow_description: 'Retning til tilfluktsrom, %s unna', diff --git a/pwa/src/i18n/nn.ts b/pwa/src/i18n/nn.ts index eace6bd..8acc662 100644 --- a/pwa/src/i18n/nn.ts +++ b/pwa/src/i18n/nn.ts @@ -41,7 +41,6 @@ export const nn: Record = { 'Ingen lagra data tilgjengeleg. Kopla til internett for å laste ned tilfluktsromdata.', update_success: 'Tilfluktsromdata oppdatert', update_failed: 'Oppdatering mislukkast — brukar lagra data', - error_shelter_not_found: 'Fann ikkje tilfluktsrommet', // Tilgjenge direction_arrow_description: 'Retning til tilfluktsrom, %s unna', diff --git a/pwa/vite.config.ts b/pwa/vite.config.ts index 4df550a..8d5b971 100644 --- a/pwa/vite.config.ts +++ b/pwa/vite.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'vite'; import { VitePWA } from 'vite-plugin-pwa'; export default defineConfig({ - base: '/', + base: './', define: { // Injected as a global — changes every build, breaking any stale cache __BUILD_REVISION__: JSON.stringify(