feat: sertakan koordinat GPS pada alert; tambahkan permission lokasi dan FusedLocationProvider; pisahkan server NTFY dan topic; perbarui MainActivity, MainViewModel, NotificationSender, MainScreen
This commit is contained in:
parent
b0531c0412
commit
56ec2b4263
@ -69,4 +69,7 @@ dependencies {
|
||||
|
||||
// AndroidX SplashScreen (required for installSplashScreen API)
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
|
||||
// Play Services Location for GPS (FusedLocationProviderClient)
|
||||
implementation("com.google.android.gms:play-services-location:21.0.1")
|
||||
}
|
||||
@ -1,21 +1,68 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var viewModel: MainViewModel
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
|
||||
// result handled inline when requesting
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||
|
||||
val fusedClient = LocationServices.getFusedLocationProviderClient(this)
|
||||
|
||||
val openEvacMaps = {
|
||||
startActivity(Intent(this, EvacuationMapsActivity::class.java))
|
||||
}
|
||||
|
||||
// onSendAlert will try to get location (if permission granted) and then call viewModel.sendAlert
|
||||
val onSendAlert = {
|
||||
// Check permission
|
||||
val fineGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
val coarseGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
if (!fineGranted && !coarseGranted) {
|
||||
// Request fine location permission (preferred)
|
||||
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
// After permission flow, user will need to press the button again to actually send with location
|
||||
viewModel.dialogMessage = "Izin lokasi dibutuhkan untuk menyertakan koordinat. Silakan tekan lagi setelah mengizinkan lokasi."
|
||||
return@let
|
||||
}
|
||||
|
||||
// Try get last location asynchronously
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
try {
|
||||
val loc = fusedClient.lastLocation.await() // need extension await - we'll handle fallback
|
||||
if (loc != null) {
|
||||
viewModel.sendAlert(loc.latitude, loc.longitude)
|
||||
} else {
|
||||
// fallback: send without coordinates
|
||||
viewModel.sendAlert()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
viewModel.sendAlert()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
MainScreen(viewModel = viewModel, onOpenEvacMaps = openEvacMaps)
|
||||
|
||||
@ -12,7 +12,7 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
@ -28,7 +28,7 @@ import androidx.compose.foundation.layout.IntrinsicSize
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainScreen(viewModel: MainViewModel, onOpenEvacMaps: () -> Unit) {
|
||||
fun MainScreen(viewModel: MainViewModel, onOpenEvacMaps: () -> Unit, onSendAlert: () -> Unit) {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bottomBar = {
|
||||
@ -38,7 +38,7 @@ fun MainScreen(viewModel: MainViewModel, onOpenEvacMaps: () -> Unit) {
|
||||
.height(120.dp),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
PanicButton(onClick = { viewModel.sendAlert() }, buttonSize = 130.dp, shadowSize = 160.dp)
|
||||
PanicButton(onClick = { onSendAlert() }, buttonSize = 130.dp, shadowSize = 160.dp)
|
||||
}
|
||||
}
|
||||
) { padding ->
|
||||
|
||||
@ -40,7 +40,8 @@ class MainViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun sendAlert() {
|
||||
// latitude/longitude optional parameters
|
||||
fun sendAlert(latitude: Double? = null, longitude: Double? = null) {
|
||||
val selectedOptions = options.filter { isChecked(it.label) }.map { it.label }
|
||||
|
||||
if (selectedOptions.isEmpty()) {
|
||||
@ -59,6 +60,12 @@ class MainViewModel : ViewModel() {
|
||||
if (otherNote.isNotBlank()) {
|
||||
bodyBuilder.append("\nCatatan: ${otherNote.trim()}")
|
||||
}
|
||||
|
||||
// Append coordinates if available
|
||||
if (latitude != null && longitude != null) {
|
||||
bodyBuilder.append("\nLokasi: https://maps.google.com/?q=$latitude,$longitude")
|
||||
}
|
||||
|
||||
val payload = bodyBuilder.toString()
|
||||
|
||||
// Use NotificationSender utility to send the payload
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user