Frist Commit
This commit is contained in:
parent
e88ada64b2
commit
5b39cd9673
@ -1,2 +1,6 @@
|
||||
package id.ac.ubharajaya.sistemakademik.data
|
||||
|
||||
data class MataKuliah(
|
||||
val kode: String,
|
||||
val nama: String
|
||||
)
|
||||
@ -11,6 +11,12 @@ data class AbsensiData(
|
||||
val longitude: Double,
|
||||
val timestamp: Long,
|
||||
val fotoBase64: String,
|
||||
|
||||
// 🔽 FIELD BARU (WAJIB DITARUH SEBELUM DEFAULT VALUE)
|
||||
val kodeMatkul: String,
|
||||
val namaMatkul: String,
|
||||
|
||||
// 🔽 FIELD DENGAN DEFAULT VALUE HARUS PALING BAWAH
|
||||
val alamat: String = "Alamat tidak tersedia"
|
||||
)
|
||||
|
||||
|
||||
@ -11,10 +11,7 @@ import android.util.Base64
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import android.widget.*
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
@ -23,6 +20,8 @@ import com.google.android.gms.location.LocationServices
|
||||
import id.ac.ubharajaya.sistemakademik.R
|
||||
import id.ac.ubharajaya.sistemakademik.data.AbsensiData
|
||||
import id.ac.ubharajaya.sistemakademik.data.DataHolder
|
||||
import id.ac.ubharajaya.sistemakademik.data.MataKuliah
|
||||
import id.ac.ubharajaya.sistemakademik.utils.DummyData
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
class AbsensiFragment : Fragment() {
|
||||
@ -31,59 +30,48 @@ class AbsensiFragment : Fragment() {
|
||||
private lateinit var tvLokasi: TextView
|
||||
private lateinit var btnAmbilFoto: Button
|
||||
private lateinit var btnLanjut: Button
|
||||
private lateinit var spinnerMatkul: Spinner
|
||||
|
||||
private var foto: Bitmap? = null
|
||||
private var latitude: Double? = null
|
||||
private var longitude: Double? = null
|
||||
private var selectedMatkul: MataKuliah? = null
|
||||
|
||||
private val fusedLocationClient by lazy {
|
||||
LocationServices.getFusedLocationProviderClient(requireActivity())
|
||||
}
|
||||
|
||||
// Permission launcher untuk lokasi
|
||||
private val locationPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted ->
|
||||
if (granted) {
|
||||
ambilLokasiGPS()
|
||||
} else {
|
||||
Toast.makeText(context, "Izin lokasi ditolak", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
// ===== Permission Launcher =====
|
||||
private val locationPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||
if (granted) ambilLokasiGPS()
|
||||
else Toast.makeText(context, "Izin lokasi ditolak", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
// Camera launcher
|
||||
private val cameraLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
private val cameraPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||
if (granted) bukaKamera()
|
||||
else Toast.makeText(context, "Izin kamera ditolak", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private val cameraLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val bitmap = result.data?.extras?.get("data") as? Bitmap
|
||||
if (bitmap != null) {
|
||||
foto = bitmap
|
||||
ivPreview.setImageBitmap(bitmap)
|
||||
bitmap?.let {
|
||||
foto = it
|
||||
ivPreview.setImageBitmap(it)
|
||||
cekKelengkapanData()
|
||||
Toast.makeText(context, "Foto berhasil diambil!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Permission launcher untuk kamera
|
||||
private val cameraPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted ->
|
||||
if (granted) {
|
||||
bukaKamera()
|
||||
} else {
|
||||
Toast.makeText(context, "Izin kamera ditolak", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_absensi, container, false)
|
||||
}
|
||||
): View = inflater.inflate(R.layout.fragment_absensi, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@ -92,87 +80,106 @@ class AbsensiFragment : Fragment() {
|
||||
tvLokasi = view.findViewById(R.id.tvLokasi)
|
||||
btnAmbilFoto = view.findViewById(R.id.btnAmbilFoto)
|
||||
btnLanjut = view.findViewById(R.id.btnLanjutPreview)
|
||||
spinnerMatkul = view.findViewById(R.id.spinnerMatkul)
|
||||
|
||||
// Cek profile sudah diisi atau belum
|
||||
if (DataHolder.userProfile.nama.isEmpty()) {
|
||||
Toast.makeText(context, "Isi profile dulu di tab Profil!", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
// Request permission lokasi
|
||||
setupSpinnerMatkul()
|
||||
requestLocationPermission()
|
||||
|
||||
btnAmbilFoto.setOnClickListener {
|
||||
requestCameraPermission()
|
||||
}
|
||||
btnAmbilFoto.setOnClickListener { requestCameraPermission() }
|
||||
|
||||
btnLanjut.setOnClickListener {
|
||||
if (foto != null && latitude != null && longitude != null) {
|
||||
simpanDataSementara()
|
||||
findNavController().navigate(R.id.action_absensi_to_preview)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Mata Kuliah =====
|
||||
private fun setupSpinnerMatkul() {
|
||||
val matkulList = DummyData.mataKuliahList
|
||||
|
||||
val adapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
matkulList.map { "${it.kode} - ${it.nama}" }
|
||||
)
|
||||
|
||||
spinnerMatkul.adapter = adapter
|
||||
|
||||
spinnerMatkul.onItemSelectedListener =
|
||||
object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(
|
||||
parent: AdapterView<*>, view: View?, position: Int, id: Long
|
||||
) {
|
||||
selectedMatkul = matkulList[position]
|
||||
cekKelengkapanData()
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
selectedMatkul = null
|
||||
cekKelengkapanData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Location =====
|
||||
private fun requestLocationPermission() {
|
||||
when {
|
||||
ContextCompat.checkSelfPermission(
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED -> {
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ambilLokasiGPS()
|
||||
}
|
||||
else -> {
|
||||
} else {
|
||||
locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ambilLokasiGPS() {
|
||||
try {
|
||||
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
|
||||
if (location != null) {
|
||||
latitude = location.latitude
|
||||
longitude = location.longitude
|
||||
tvLokasi.text = "📍 GPS Ready\nLat: ${location.latitude}\nLon: ${location.longitude}"
|
||||
tvLokasi.text =
|
||||
"📍 GPS Ready\nLat: ${location.latitude}\nLon: ${location.longitude}"
|
||||
cekKelengkapanData()
|
||||
} else {
|
||||
tvLokasi.text = "❌ Lokasi tidak tersedia\nHidupkan GPS"
|
||||
tvLokasi.text = "❌ Lokasi tidak tersedia"
|
||||
}
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Camera =====
|
||||
private fun requestCameraPermission() {
|
||||
when {
|
||||
ContextCompat.checkSelfPermission(
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.CAMERA
|
||||
) == PackageManager.PERMISSION_GRANTED -> {
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
bukaKamera()
|
||||
}
|
||||
else -> {
|
||||
} else {
|
||||
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bukaKamera() {
|
||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
cameraLauncher.launch(intent)
|
||||
cameraLauncher.launch(Intent(MediaStore.ACTION_IMAGE_CAPTURE))
|
||||
}
|
||||
|
||||
// ===== Validation =====
|
||||
private fun cekKelengkapanData() {
|
||||
btnLanjut.isEnabled = foto != null && latitude != null && longitude != null
|
||||
btnLanjut.isEnabled =
|
||||
foto != null &&
|
||||
latitude != null &&
|
||||
longitude != null &&
|
||||
selectedMatkul != null
|
||||
}
|
||||
|
||||
// ===== Save =====
|
||||
private fun simpanDataSementara() {
|
||||
val bitmap = foto ?: return
|
||||
val lat = latitude ?: return
|
||||
val lon = longitude ?: return
|
||||
|
||||
val fotoBase64 = bitmapToBase64(bitmap)
|
||||
val matkul = selectedMatkul ?: return
|
||||
|
||||
DataHolder.currentAbsensi = AbsensiData(
|
||||
nama = DataHolder.userProfile.nama,
|
||||
@ -180,8 +187,10 @@ class AbsensiFragment : Fragment() {
|
||||
latitude = lat,
|
||||
longitude = lon,
|
||||
timestamp = System.currentTimeMillis(),
|
||||
fotoBase64 = fotoBase64,
|
||||
alamat = "Alamat akan diambil saat preview"
|
||||
fotoBase64 = bitmapToBase64(bitmap),
|
||||
alamat = "Alamat akan diambil saat preview",
|
||||
kodeMatkul = matkul.kode,
|
||||
namaMatkul = matkul.nama
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -123,17 +123,38 @@ class PreviewFragment : Fragment() {
|
||||
conn.doOutput = true
|
||||
|
||||
val json = JSONObject().apply {
|
||||
|
||||
// 1️⃣ HARUS ADA
|
||||
put("timestamp", absensi.timestamp)
|
||||
|
||||
// 2️⃣ IP address (kalau belum ada, kirim dummy)
|
||||
put("ip_addr", "android")
|
||||
|
||||
// 3️⃣ Identitas
|
||||
put("npm", absensi.npm)
|
||||
put("nama", absensi.nama)
|
||||
|
||||
// 4️⃣ Lokasi
|
||||
put("latitude", absensi.latitude)
|
||||
put("longitude", absensi.longitude)
|
||||
put("timestamp", absensi.timestamp)
|
||||
put("foto_base64", absensi.fotoBase64)
|
||||
put("address", absensi.alamat)
|
||||
put("distance_from_campus", 0)
|
||||
|
||||
// 5️⃣ MATA KULIAH (INI YANG KOSONG KEMARIN)
|
||||
put("mata_kuliah", absensi.namaMatkul)
|
||||
// atau kalau mau sekalian kode:
|
||||
// put("mata_kuliah", "${absensi.kodeMatkul} - ${absensi.namaMatkul}")
|
||||
|
||||
// 6️⃣ Photo (boleh string apa aja / URL / label)
|
||||
put("photo", "camera")
|
||||
|
||||
// 7️⃣ Status
|
||||
put("status", "hadir")
|
||||
|
||||
// 8️⃣ FOTO BASE64
|
||||
put("foto_base64", absensi.fotoBase64)
|
||||
}
|
||||
|
||||
|
||||
|
||||
conn.outputStream.use {
|
||||
it.write(json.toString().toByteArray())
|
||||
}
|
||||
|
||||
@ -1,2 +1,11 @@
|
||||
package id.ac.ubharajaya.sistemakademik.utils
|
||||
|
||||
import id.ac.ubharajaya.sistemakademik.data.MataKuliah
|
||||
|
||||
object DummyData {
|
||||
val mataKuliahList = listOf(
|
||||
MataKuliah("IF301", "Pemrograman Mobile"),
|
||||
MataKuliah("IF302", "Kecerdasan Buatan"),
|
||||
MataKuliah("IF303", "Basis Data")
|
||||
)
|
||||
}
|
||||
@ -32,6 +32,13 @@
|
||||
android:textAlignment="center"
|
||||
android:layout_marginBottom="32dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerMatkul"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnAmbilFoto"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user