458 lines
13 KiB
Markdown
458 lines
13 KiB
Markdown
# 📖 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
|
||
|