# πŸ“– Panduan Implementasi Aplikasi Absensi Akademik ## 🎯 Gambaran Umum Implementasi Aplikasi ini telah diimplementasikan dengan arsitektur yang modular dan clean, memisahkan concerns menjadi beberapa layers: 1. **UI Layer** (Compose Components) 2. **Network Layer** (API Communication) 3. **Utils Layer** (Business Logic) 4. **Config Layer** (Configuration Management) 5. **Models Layer** (Data Classes) --- ## πŸ“‚ File Structure Lengkap ### Core Application ``` MainActivity.kt # Entry point & main UI screen ``` ### Configuration ``` config/ └─ AttendanceConfig.kt # Centralized configuration ``` ### Data Models ``` models/ └─ AttendanceRecord.kt # Data classes: # - AttendanceRecord # - LocationData # - ValidationResult # - AttendanceState # - ValidationStatus enum ``` ### Network Communication ``` network/ └─ N8nService.kt # API service untuk N8n webhook ``` ### Utilities ``` utils/ β”œβ”€ LocationValidator.kt # Location validation logic: # - calculateDistance (Haversine) # - isLocationValid # - getValidationMessage # - adjustCoordinates └─ ErrorHandler.kt # Error handling & messages ``` ### UI Components ``` ui/ β”œβ”€ components/ β”‚ └─ AttendanceComponents.kt # Reusable components: β”‚ # - PhotoPreviewCard β”‚ # - LocationStatusCard β”‚ # - ErrorAlertCard β”‚ # - SubmitButtonWithLoader └─ theme/ β”œβ”€ Theme.kt # Material 3 theme β”œβ”€ Color.kt # Color definitions └─ Type.kt # Typography definitions ``` ### Tests ``` test/ └─ java/id/ac/ubharajaya/sistemakademik/ └─ utils/ └─ LocationValidatorTest.kt # Unit tests ``` --- ## πŸ”„ Flow Diagram ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ MainActivity β”‚ β”‚ (Jetpack Compose UI + State Management) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Location β”‚ β”‚ Camera β”‚ β”‚ Permissions β”‚ β”‚ Permissions β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”‚ LocationValidator β”‚ β”‚ (Haversine distance calc) β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Validation Result β”‚ β”‚ (Valid/Invalid) β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ N8nService β”‚ β”‚ (HTTP POST to Webhook) β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ N8n Server β”‚ β”‚ (Webhook Processing) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸš€ Penggunaan Aplikasi ### Alur Pengguna 1. **Buka Aplikasi** β†’ MainActivity dimuat 2. **Minta Izin Lokasi** β†’ LocationPermissionLauncher diaktifkan otomatis 3. **Ambil Lokasi** β†’ Fused Location Provider mengambil GPS 4. **Validasi Lokasi** β†’ LocationValidator mengecek jarak 5. **Tampilkan Status** β†’ LocationStatusCard menampilkan hasil 6. **Ambil Foto** β†’ CameraPermissionLauncher + Intent Camera 7. **Preview Foto** β†’ PhotoPreviewCard menampilkan hasil 8. **Validasi Data** β†’ Cek semua field terpenuhi 9. **Kirim Data** β†’ N8nService POST ke webhook 10. **Tampilkan Hasil** β†’ Success/Error message --- ## βš™οΈ State Management ### AttendanceState Data Class ```kotlin data class AttendanceState( val location: LocationData? = null, // GPS coordinates val foto: Bitmap? = null, // Camera image val isLoadingLocation: Boolean = false, // GPS acquiring val isLoadingSubmit: Boolean = false, // API submitting val validationResult: ValidationResult = ..., // Location validation val errorMessage: String? = null, // Error feedback val isLocationPermissionGranted: Boolean = false, val isCameraPermissionGranted: Boolean = false ) ``` ### State Updates State diupdate secara reactive menggunakan: ```kotlin var state by remember { mutableStateOf(AttendanceState()) } state = state.copy(location = newLocation) // Immutable update ``` --- ## πŸ“ Validasi Lokasi Detil ### Haversine Formula Untuk menghitung jarak akurat antara dua koordinat: ``` Formula: a = sinΒ²(Δφ/2) + cos(Ο†1) Γ— cos(Ο†2) Γ— sinΒ²(Δλ/2) c = 2 Γ— atan2(√a, √(1βˆ’a)) d = R Γ— c Di mana: - Ο† adalah latitude (dalam radian) - Ξ» adalah longitude (dalam radian) - R adalah radius bumi (6,371 km) ``` ### Contoh Validasi ```kotlin // Referensi lokasi kampus const val REFERENCE_LATITUDE = -7.0 const val REFERENCE_LONGITUDE = 110.4 // Lokasi mahasiswa (dari GPS) val studentLatitude = -7.0035 val studentLongitude = 110.4042 // Hitung jarak val distance = LocationValidator.calculateDistance( REFERENCE_LATITUDE, REFERENCE_LONGITUDE, studentLatitude, studentLongitude ) // Result: ~500 meter // Validasi terhadap radius (100m) val isValid = distance <= 100.0 // false ``` --- ## πŸ“‘ API Integration Detail ### Request Format ```json { "npm": "202310715082", "nama": "Fazri Abdurrahman", "latitude": -7.0035, "longitude": 110.4042, "timestamp": 1705250400000, "foto_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEA..." } ``` ### Response Handling ```kotlin HttpURLConnection.HTTP_OK (200) // Success - absensi diterima HttpURLConnection.HTTP_BAD_REQUEST (400) // Error - data invalid HttpURLConnection.HTTP_UNAUTHORIZED (401) // Error - auth failed HttpURLConnection.HTTP_INTERNAL_ERROR (500) // Error - server error ``` ### Error Callback ```kotlin interface SubmitCallback { fun onSuccess(responseCode: Int, message: String) fun onError(error: Throwable, message: String) } ``` --- ## 🎨 UI Components Detail ### 1. PhotoPreviewCard ```kotlin PhotoPreviewCard( bitmap = state.foto, // Bitmap dari camera onRetake = { /* reset foto */ } ) ``` - Menampilkan preview foto - Button "Ambil Ulang" untuk mengganti foto - Placeholder jika belum ada foto ### 2. LocationStatusCard ```kotlin LocationStatusCard( latitude = state.location?.latitude, longitude = state.location?.longitude, validationMessage = state.validationResult.message, isLoading = state.isLoadingLocation ) ``` - Menampilkan koordinat GPS - Pesan validasi (βœ“ valid atau βœ— invalid) - Loading spinner saat mengambil lokasi ### 3. ErrorAlertCard ```kotlin ErrorAlertCard( message = state.errorMessage, onDismiss = { /* hide error */ } ) ``` - Card merah untuk error messages - Dismissable dengan tombol X - Auto-hide saat tidak ada error ### 4. SubmitButtonWithLoader ```kotlin SubmitButtonWithLoader( text = "Kirim Absensi", onClick = { /* submit */ }, isLoading = state.isLoadingSubmit, isEnabled = canSubmit ) ``` - Button dengan loading indicator - Disabled saat proses - Spinner bertekstur saat loading --- ## πŸ” Permission Handling ### Automatic Permission Request ```kotlin LaunchedEffect(Unit) { locationPermissionLauncher.launch( Manifest.permission.ACCESS_FINE_LOCATION ) } ``` - Dipanggil otomatis saat app launch - Request CAMERA saat user klik "Ambil Foto" ### Permission Check ```kotlin if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) ``` --- ## πŸ§ͺ Testing ### Unit Test LocationValidator ```bash ./gradlew test ``` Test cases: - βœ“ Distance calculation accuracy - βœ“ Validation logic (within/outside radius) - βœ“ Message generation - βœ“ Coordinate adjustment - βœ“ Mathematical properties (symmetry, triangle inequality) ### Manual Testing Checklist ``` [ ] Permissions diminta dengan benar [ ] Lokasi GPS terakses saat connected [ ] Card lokasi menampilkan koordinat [ ] Validasi menunjukkan jarak akurat [ ] Camera intent terbuka saat "Ambil Foto" diklik [ ] Foto preview ditampilkan setelah capture [ ] Form disabled saat lokasi invalid [ ] Submit button hanya enable jika semua valid [ ] Loading spinner muncul saat submit [ ] Success message muncul setelah 200 response [ ] Error message muncul setelah gagal [ ] Form reset setelah 2 detik sukses ``` --- ## πŸ› οΈ Customization Guide ### Ubah Koordinat Referensi **File**: `AttendanceConfig.kt` ```kotlin const val REFERENCE_LATITUDE = -7.025 // Ubah ke lokasi kampus const val REFERENCE_LONGITUDE = 110.415 ``` ### Ubah Radius Area ```kotlin const val ALLOWED_RADIUS_METERS = 150.0 // Ubah ke radius yang diinginkan ``` ### Ubah Data Mahasiswa ```kotlin const val STUDENT_NPM = "202310715082" const val STUDENT_NAMA = "Fazri Abdurrahman" ``` ### Ubah Webhook URL ```kotlin const val WEBHOOK_PRODUCTION = "https://your-webhook-url/..." const val WEBHOOK_TEST = "https://your-test-webhook-url/..." ``` ### Ubah Photo Quality ```kotlin const val PHOTO_QUALITY = 80 // 0-100, lebih tinggi = lebih besar file ``` --- ## πŸ“Š Monitoring & Debugging ### Enable Logging ```kotlin // Di N8nService, tambahkan: Log.d("N8nService", "Request: $json") Log.d("N8nService", "Response Code: $responseCode") ``` ### Check Permission Status ```kotlin val hasLocationPermission = ContextCompat.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED ``` ### Validate GPS - Buka Google Maps untuk confirm GPS aktif - Verifikasi koordinat akurat di Maps - Test di lokasi berbeda untuk validate radius ### Test Webhook 1. Gunakan `WEBHOOK_TEST` terlebih dahulu 2. Cek response di N8n dashboard 3. Verify data received dengan benar 4. Switch ke `WEBHOOK_PRODUCTION` saat siap --- ## 🚨 Common Issues & Solutions | Issue | Cause | Solution | |-------|-------|----------| | GPS tidak berfungsi | Location permission ditolak | Buka Settings > Permissions > Location | | Lokasi selalu invalid | Koordinat referensi salah | Update `REFERENCE_LATITUDE/LONGITUDE` | | Foto tidak terakses | Camera permission ditolak | Buka Settings > Permissions > Camera | | Submit gagal | Network issue | Check internet connection | | Webhook 404 | URL salah | Verify webhook URL di `AttendanceConfig` | --- ## πŸ“š Dependencies ```gradle // Sudah terinclude di build.gradle.kts: - androidx.core:core-ktx - androidx.lifecycle:lifecycle-runtime-ktx - androidx.compose.* (UI framework) - com.google.android.gms:play-services-location (GPS) - Material 3 (Design system) ``` --- ## πŸ”„ Next Steps / Future Features ### Phase 2 (Future) - [ ] Attendance history dengan Room Database - [ ] User login screen dengan authentication - [ ] Support multiple courses/classes - [ ] Attendance statistics & reports - [ ] Push notifications untuk deadline - [ ] Offline mode dengan sync ### Phase 3 (Advanced) - [ ] Biometric verification (fingerprint) - [ ] QR code verification - [ ] Face recognition - [ ] Real-time attendance dashboard - [ ] Mobile app backend server --- ## πŸ“ž Support & Contact Untuk pertanyaan atau issues: 1. Check logs di Android Studio Logcat 2. Review error messages di app 3. Test dengan webhook test terlebih dahulu 4. Verify configurations di `AttendanceConfig.kt` --- **Last Updated**: January 14, 2026 **Version**: 1.0 **Status**: βœ… Production Ready