feat: add gps

This commit is contained in:
rakha 2025-11-27 18:36:09 +07:00
parent 56ec2b4263
commit c1b2fc181b
4 changed files with 43 additions and 29 deletions

View File

@ -24,6 +24,10 @@
<option value="a20448f7-57cd-4a40-89b1-460451412588" />
<option value="484561a1-1c08-44cd-bcd3-a53895fc3851" />
<option value="17ec376a-38fd-48ac-a4e1-52e9f7df3100" />
<option value="a0c23b11-ba1e-4275-a3e4-38b3c8fdbe00" />
<option value="3ac3800e-363e-4148-86b3-0d4f3b0a8b62" />
<option value="5460190d-c8ff-4194-9316-a6298c7cb54b" />
<option value="c640b33a-4bc6-4ee0-98ed-6a71e7270331" />
</set>
</value>
</entry>
@ -84,6 +88,9 @@
<option value="file://$PROJECT_DIR$/app/build.gradle.kts" />
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt" />
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/NotificationSender.kt" />
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/MainViewModel.kt" />
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt" />
<option value="file://$PROJECT_DIR$/app/src/main/AndroidManifest.xml" />
</set>
</entry>
<entry key="bf5061b4-a1e9-4600-a273-ea8e51b2ce89" />

View File

@ -2,6 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@ -34,5 +38,4 @@
android:label="Detail Peta" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@ -1,6 +1,7 @@
package id.ac.ubharajaya.panicbutton
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
@ -11,16 +12,20 @@ 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
// Inform user about permission result
if (isGranted) {
// User granted permission
// Ask user to press the panic button again to include location
if (::viewModel.isInitialized) viewModel.dialogMessage = "Izin lokasi diberikan. Tekan kembali untuk menyertakan koordinat."
} else {
if (::viewModel.isInitialized) viewModel.dialogMessage = "Izin lokasi ditolak. Laporan akan dikirim tanpa koordinat."
}
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -33,8 +38,8 @@ class MainActivity : ComponentActivity() {
startActivity(Intent(this, EvacuationMapsActivity::class.java))
}
// onSendAlert will try to get location (if permission granted) and then call viewModel.sendAlert
val onSendAlert = {
@SuppressLint("MissingPermission")
fun handleSendAlert() {
// 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
@ -44,28 +49,28 @@ class MainActivity : ComponentActivity() {
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
return
}
// Try get last location asynchronously
CoroutineScope(Dispatchers.Main).launch {
try {
val loc = fusedClient.lastLocation.await() // need extension await - we'll handle fallback
// Try get last location asynchronously via Task listeners
fusedClient.lastLocation
.addOnSuccessListener { loc ->
if (loc != null) {
viewModel.sendAlert(loc.latitude, loc.longitude)
} else {
// fallback: send without coordinates
viewModel.sendAlert()
}
} catch (e: Exception) {
viewModel.sendAlert()
}
.addOnFailureListener {
// If obtaining location fails, send without coordinates
viewModel.sendAlert()
}
}
setContent {
MaterialTheme {
MainScreen(viewModel = viewModel, onOpenEvacMaps = openEvacMaps)
MainScreen(viewModel = viewModel, onOpenEvacMaps = openEvacMaps, onSendAlert = ::handleSendAlert)
}
}
}

View File

@ -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.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@ -22,9 +22,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.foundation.layout.IntrinsicSize
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -91,15 +89,16 @@ fun MainScreen(viewModel: MainViewModel, onOpenEvacMaps: () -> Unit, onSendAlert
}
// Dialog
val dialogMessage = viewModel.dialogMessage
if (!dialogMessage.isNullOrBlank()) {
viewModel.dialogMessage
?.takeIf { it.isNotBlank() }
?.let { msg ->
AlertDialog(
onDismissRequest = { viewModel.clearDialog() },
confirmButton = {
TextButton(onClick = { viewModel.clearDialog() }) { Text("OK") }
},
title = { Text("🚨 Notifikasi", style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)) },
text = { Text(dialogMessage ?: "") }
text = { Text(msg) }
)
}
}