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>
This commit is contained in:
Ole-Morten Duesund 2026-03-09 10:01:58 +01:00
commit 3c1da8adec
5 changed files with 17 additions and 1 deletions

View file

@ -3,6 +3,7 @@ package no.naiv.tilfluktsrom
import android.Manifest import android.Manifest
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.view.HapticFeedbackConstants
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.hardware.Sensor import android.hardware.Sensor
import android.hardware.SensorEvent import android.hardware.SensorEvent
@ -202,6 +203,7 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
private fun setupButtons() { private fun setupButtons() {
binding.toggleViewFab.setOnClickListener { binding.toggleViewFab.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
isCompassMode = !isCompassMode isCompassMode = !isCompassMode
if (isCompassMode) { if (isCompassMode) {
binding.mapView.visibility = View.GONE binding.mapView.visibility = View.GONE
@ -216,24 +218,29 @@ class MainActivity : AppCompatActivity(), SensorEventListener {
// Reset to navigation: re-fit map to show user + selected shelter // Reset to navigation: re-fit map to show user + selected shelter
binding.resetNavigationFab.setOnClickListener { binding.resetNavigationFab.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
userHasInteractedWithMap = false userHasInteractedWithMap = false
binding.resetNavigationFab.visibility = View.GONE binding.resetNavigationFab.visibility = View.GONE
selectedShelter?.let { highlightShelterOnMap(it) } selectedShelter?.let { highlightShelterOnMap(it) }
} }
binding.infoButton.setOnClickListener { binding.infoButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
CivilDefenseInfoDialog().show(supportFragmentManager, CivilDefenseInfoDialog.TAG) CivilDefenseInfoDialog().show(supportFragmentManager, CivilDefenseInfoDialog.TAG)
} }
binding.refreshButton.setOnClickListener { binding.refreshButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
forceRefresh() forceRefresh()
} }
binding.shareButton.setOnClickListener { binding.shareButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
shareShelter() shareShelter()
} }
binding.cacheRetryButton.setOnClickListener { binding.cacheRetryButton.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
val loc = currentLocation val loc = currentLocation
if (loc == null) { if (loc == null) {
Toast.makeText(this, R.string.status_no_location, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.status_no_location, Toast.LENGTH_SHORT).show()

View file

@ -1,5 +1,6 @@
package no.naiv.tilfluktsrom.ui package no.naiv.tilfluktsrom.ui
import android.view.HapticFeedbackConstants
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
@ -63,6 +64,7 @@ class ShelterListAdapter(
binding.root.alpha = if (isSelected) 1.0f else 0.7f binding.root.alpha = if (isSelected) 1.0f else 0.7f
binding.root.setOnClickListener { binding.root.setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
val pos = adapterPosition val pos = adapterPosition
if (pos != RecyclerView.NO_POSITION) { if (pos != RecyclerView.NO_POSITION) {
selectPosition(pos) selectPosition(pos)

View file

@ -76,6 +76,7 @@ function setupButtons(): void {
// Toggle map/compass // Toggle map/compass
const toggleFab = document.getElementById('toggle-fab')!; const toggleFab = document.getElementById('toggle-fab')!;
toggleFab.addEventListener('click', async () => { toggleFab.addEventListener('click', async () => {
navigator.vibrate?.(10);
isCompassMode = !isCompassMode; isCompassMode = !isCompassMode;
const mapContainer = document.getElementById('map-container')!; const mapContainer = document.getElementById('map-container')!;
@ -112,6 +113,7 @@ function setupButtons(): void {
const cacheRetryBtn = document.getElementById('cache-retry-btn')!; const cacheRetryBtn = document.getElementById('cache-retry-btn')!;
cacheRetryBtn.textContent = t('action_cache_now'); cacheRetryBtn.textContent = t('action_cache_now');
cacheRetryBtn.addEventListener('click', () => { cacheRetryBtn.addEventListener('click', () => {
navigator.vibrate?.(10);
if (currentLocation && navigator.onLine) { if (currentLocation && navigator.onLine) {
startCaching(currentLocation.latitude, currentLocation.longitude); startCaching(currentLocation.latitude, currentLocation.longitude);
} }
@ -120,6 +122,7 @@ function setupButtons(): void {
// Reset view button // Reset view button
const resetBtn = document.getElementById('reset-view-btn')!; const resetBtn = document.getElementById('reset-view-btn')!;
resetBtn.addEventListener('click', () => { resetBtn.addEventListener('click', () => {
navigator.vibrate?.(10);
const selected = nearestShelters[selectedShelterIndex] ?? null; const selected = nearestShelters[selectedShelterIndex] ?? null;
mapView.resetView(selected, currentLocation); mapView.resetView(selected, currentLocation);
resetBtn.classList.remove('visible'); resetBtn.classList.remove('visible');

View file

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

View file

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