Frist Commit
This commit is contained in:
parent
e88ada64b2
commit
5b39cd9673
@ -1,2 +1,6 @@
|
|||||||
package id.ac.ubharajaya.sistemakademik.data
|
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 longitude: Double,
|
||||||
val timestamp: Long,
|
val timestamp: Long,
|
||||||
val fotoBase64: String,
|
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"
|
val alamat: String = "Alamat tidak tersedia"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,7 @@ import android.util.Base64
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Button
|
import android.widget.*
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
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.R
|
||||||
import id.ac.ubharajaya.sistemakademik.data.AbsensiData
|
import id.ac.ubharajaya.sistemakademik.data.AbsensiData
|
||||||
import id.ac.ubharajaya.sistemakademik.data.DataHolder
|
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
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
class AbsensiFragment : Fragment() {
|
class AbsensiFragment : Fragment() {
|
||||||
@ -31,59 +30,48 @@ class AbsensiFragment : Fragment() {
|
|||||||
private lateinit var tvLokasi: TextView
|
private lateinit var tvLokasi: TextView
|
||||||
private lateinit var btnAmbilFoto: Button
|
private lateinit var btnAmbilFoto: Button
|
||||||
private lateinit var btnLanjut: Button
|
private lateinit var btnLanjut: Button
|
||||||
|
private lateinit var spinnerMatkul: Spinner
|
||||||
|
|
||||||
private var foto: Bitmap? = null
|
private var foto: Bitmap? = null
|
||||||
private var latitude: Double? = null
|
private var latitude: Double? = null
|
||||||
private var longitude: Double? = null
|
private var longitude: Double? = null
|
||||||
|
private var selectedMatkul: MataKuliah? = null
|
||||||
|
|
||||||
private val fusedLocationClient by lazy {
|
private val fusedLocationClient by lazy {
|
||||||
LocationServices.getFusedLocationProviderClient(requireActivity())
|
LocationServices.getFusedLocationProviderClient(requireActivity())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permission launcher untuk lokasi
|
// ===== Permission Launcher =====
|
||||||
private val locationPermissionLauncher = registerForActivityResult(
|
private val locationPermissionLauncher =
|
||||||
ActivityResultContracts.RequestPermission()
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||||
) { granted ->
|
if (granted) ambilLokasiGPS()
|
||||||
if (granted) {
|
else Toast.makeText(context, "Izin lokasi ditolak", Toast.LENGTH_SHORT).show()
|
||||||
ambilLokasiGPS()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, "Izin lokasi ditolak", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Camera launcher
|
private val cameraPermissionLauncher =
|
||||||
private val cameraLauncher = registerForActivityResult(
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||||
ActivityResultContracts.StartActivityForResult()
|
if (granted) bukaKamera()
|
||||||
) { result ->
|
else Toast.makeText(context, "Izin kamera ditolak", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cameraLauncher =
|
||||||
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
val bitmap = result.data?.extras?.get("data") as? Bitmap
|
val bitmap = result.data?.extras?.get("data") as? Bitmap
|
||||||
if (bitmap != null) {
|
bitmap?.let {
|
||||||
foto = bitmap
|
foto = it
|
||||||
ivPreview.setImageBitmap(bitmap)
|
ivPreview.setImageBitmap(it)
|
||||||
cekKelengkapanData()
|
cekKelengkapanData()
|
||||||
Toast.makeText(context, "Foto berhasil diambil!", Toast.LENGTH_SHORT).show()
|
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(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View = inflater.inflate(R.layout.fragment_absensi, container, false)
|
||||||
return inflater.inflate(R.layout.fragment_absensi, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@ -92,87 +80,106 @@ class AbsensiFragment : Fragment() {
|
|||||||
tvLokasi = view.findViewById(R.id.tvLokasi)
|
tvLokasi = view.findViewById(R.id.tvLokasi)
|
||||||
btnAmbilFoto = view.findViewById(R.id.btnAmbilFoto)
|
btnAmbilFoto = view.findViewById(R.id.btnAmbilFoto)
|
||||||
btnLanjut = view.findViewById(R.id.btnLanjutPreview)
|
btnLanjut = view.findViewById(R.id.btnLanjutPreview)
|
||||||
|
spinnerMatkul = view.findViewById(R.id.spinnerMatkul)
|
||||||
|
|
||||||
// Cek profile sudah diisi atau belum
|
setupSpinnerMatkul()
|
||||||
if (DataHolder.userProfile.nama.isEmpty()) {
|
|
||||||
Toast.makeText(context, "Isi profile dulu di tab Profil!", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request permission lokasi
|
|
||||||
requestLocationPermission()
|
requestLocationPermission()
|
||||||
|
|
||||||
btnAmbilFoto.setOnClickListener {
|
btnAmbilFoto.setOnClickListener { requestCameraPermission() }
|
||||||
requestCameraPermission()
|
|
||||||
}
|
|
||||||
|
|
||||||
btnLanjut.setOnClickListener {
|
btnLanjut.setOnClickListener {
|
||||||
if (foto != null && latitude != null && longitude != null) {
|
|
||||||
simpanDataSementara()
|
simpanDataSementara()
|
||||||
findNavController().navigate(R.id.action_absensi_to_preview)
|
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() {
|
private fun requestLocationPermission() {
|
||||||
when {
|
if (ContextCompat.checkSelfPermission(
|
||||||
ContextCompat.checkSelfPermission(
|
|
||||||
requireContext(),
|
requireContext(),
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
) == PackageManager.PERMISSION_GRANTED -> {
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
ambilLokasiGPS()
|
ambilLokasiGPS()
|
||||||
}
|
} else {
|
||||||
else -> {
|
|
||||||
locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun ambilLokasiGPS() {
|
private fun ambilLokasiGPS() {
|
||||||
try {
|
|
||||||
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
|
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
latitude = location.latitude
|
latitude = location.latitude
|
||||||
longitude = location.longitude
|
longitude = location.longitude
|
||||||
tvLokasi.text = "📍 GPS Ready\nLat: ${location.latitude}\nLon: ${location.longitude}"
|
tvLokasi.text =
|
||||||
|
"📍 GPS Ready\nLat: ${location.latitude}\nLon: ${location.longitude}"
|
||||||
cekKelengkapanData()
|
cekKelengkapanData()
|
||||||
} else {
|
} 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() {
|
private fun requestCameraPermission() {
|
||||||
when {
|
if (ContextCompat.checkSelfPermission(
|
||||||
ContextCompat.checkSelfPermission(
|
|
||||||
requireContext(),
|
requireContext(),
|
||||||
Manifest.permission.CAMERA
|
Manifest.permission.CAMERA
|
||||||
) == PackageManager.PERMISSION_GRANTED -> {
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
bukaKamera()
|
bukaKamera()
|
||||||
}
|
} else {
|
||||||
else -> {
|
|
||||||
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun bukaKamera() {
|
private fun bukaKamera() {
|
||||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
cameraLauncher.launch(Intent(MediaStore.ACTION_IMAGE_CAPTURE))
|
||||||
cameraLauncher.launch(intent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Validation =====
|
||||||
private fun cekKelengkapanData() {
|
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() {
|
private fun simpanDataSementara() {
|
||||||
val bitmap = foto ?: return
|
val bitmap = foto ?: return
|
||||||
val lat = latitude ?: return
|
val lat = latitude ?: return
|
||||||
val lon = longitude ?: return
|
val lon = longitude ?: return
|
||||||
|
val matkul = selectedMatkul ?: return
|
||||||
val fotoBase64 = bitmapToBase64(bitmap)
|
|
||||||
|
|
||||||
DataHolder.currentAbsensi = AbsensiData(
|
DataHolder.currentAbsensi = AbsensiData(
|
||||||
nama = DataHolder.userProfile.nama,
|
nama = DataHolder.userProfile.nama,
|
||||||
@ -180,8 +187,10 @@ class AbsensiFragment : Fragment() {
|
|||||||
latitude = lat,
|
latitude = lat,
|
||||||
longitude = lon,
|
longitude = lon,
|
||||||
timestamp = System.currentTimeMillis(),
|
timestamp = System.currentTimeMillis(),
|
||||||
fotoBase64 = fotoBase64,
|
fotoBase64 = bitmapToBase64(bitmap),
|
||||||
alamat = "Alamat akan diambil saat preview"
|
alamat = "Alamat akan diambil saat preview",
|
||||||
|
kodeMatkul = matkul.kode,
|
||||||
|
namaMatkul = matkul.nama
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -123,17 +123,38 @@ class PreviewFragment : Fragment() {
|
|||||||
conn.doOutput = true
|
conn.doOutput = true
|
||||||
|
|
||||||
val json = JSONObject().apply {
|
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("npm", absensi.npm)
|
||||||
put("nama", absensi.nama)
|
put("nama", absensi.nama)
|
||||||
|
|
||||||
|
// 4️⃣ Lokasi
|
||||||
put("latitude", absensi.latitude)
|
put("latitude", absensi.latitude)
|
||||||
put("longitude", absensi.longitude)
|
put("longitude", absensi.longitude)
|
||||||
put("timestamp", absensi.timestamp)
|
|
||||||
put("foto_base64", absensi.fotoBase64)
|
// 5️⃣ MATA KULIAH (INI YANG KOSONG KEMARIN)
|
||||||
put("address", absensi.alamat)
|
put("mata_kuliah", absensi.namaMatkul)
|
||||||
put("distance_from_campus", 0)
|
// 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")
|
put("status", "hadir")
|
||||||
|
|
||||||
|
// 8️⃣ FOTO BASE64
|
||||||
|
put("foto_base64", absensi.fotoBase64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
conn.outputStream.use {
|
conn.outputStream.use {
|
||||||
it.write(json.toString().toByteArray())
|
it.write(json.toString().toByteArray())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,11 @@
|
|||||||
package id.ac.ubharajaya.sistemakademik.utils
|
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:textAlignment="center"
|
||||||
android:layout_marginBottom="32dp"/>
|
android:layout_marginBottom="32dp"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerMatkul"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btnAmbilFoto"
|
android:id="@+id/btnAmbilFoto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user