14 KiB
🔧 Troubleshooting Guide
Common Issues & Solutions
🚨 Build Issues
Issue 1: Gradle Sync Failed
Error: Gradle sync failed: Failed to resolve...
Solutions:
- Clean project:
Build→Clean Project - Invalidate caches:
File→Invalidate Caches / Restart - Sync Gradle:
File→Sync Now - Delete
.gradlefolder and try again - Check internet connection (gradle downloads dependencies)
Issue 2: Missing Dependencies
Error: Unresolved reference: LocationValidator atau similar
Solutions:
- Verify file exists in correct package structure
- Check package declaration at top of file
- Rebuild project:
Build→Rebuild Project - Check that all imports are present
- File → Invalidate Caches / Restart
Issue 3: Kotlin Syntax Error
Error: Type mismatch: inferred type is String? but Boolean was expected
Solutions:
- Check null-safety: use
?.or!!appropriately - Verify data class properties match their types
- Look for missing type annotations
- Check imports for correct classes
📍 Location Issues
Issue 1: GPS Not Acquiring Location
Error: "Lokasi tidak tersedia"
Causes & Solutions:
-
❌ Location permission not granted → Check Settings → Apps → Permissions → Location (Allow)
-
❌ Location services disabled → Enable in Settings → Location → Location Services
-
❌ Cold start GPS (takes time to acquire) → Wait 30-60 seconds for GPS to warm up → Or enable "Use high accuracy" in location settings
-
❌ Testing in emulator → Use Extended Controls → Location to simulate GPS → Or use GPX file for GPS simulation
Quick Check:
// Test if GPS is available
val hasLocationPermission = ContextCompat.checkSelfPermission(
context, Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
// Test if location services enabled
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
Issue 2: Location Always Invalid
Error: "✗ Lokasi tidak valid (2500m, maksimal 100m)"
Causes & Solutions:
-
❌ Reference coordinates wrong → Update
REFERENCE_LATITUDEdanREFERENCE_LONGITUDEdiAttendanceConfig.kt→ Verify with Google Maps -
❌ Radius too small → Increase
ALLOWED_RADIUS_METERStemporarily for testing → Default is 100m, try 200m or 300m -
❌ Testing from wrong location → Go to actual campus location → Or use emulator GPS simulation
How to Fix:
// File: AttendanceConfig.kt
const val REFERENCE_LATITUDE = -7.025 // ← Update this
const val REFERENCE_LONGITUDE = 110.415 // ← Update this
const val ALLOWED_RADIUS_METERS = 100.0 // ← Or increase this
How to Verify Reference Coordinates:
- Go to campus location with device
- Open Google Maps
- Long-click on map → Coordinates appear at top
- Copy latitude and longitude
- Update in
AttendanceConfig.kt
Issue 3: Distance Calculation Wrong
Error: Shows 5000m distance when clearly close to reference
Causes & Solutions:
-
❌ Latitude/longitude swapped → Check: latitude is first, longitude is second → Format should be: (-7.025, 110.415) not (110.415, -7.025)
-
❌ Wrong location acquired → Check Logcat:
Log.d("Location", "Lat: $lat, Lon: $lon")→ Verify with Google Maps -
❌ Haversine formula bug → Run unit tests:
./gradlew test→ Check LocationValidatorTest results
Debug Steps:
// Add logging to MainActivity
Log.d("AbsensiApp", "Reference: -7.0, 110.4")
Log.d("AbsensiApp", "Student: ${state.location?.latitude}, ${state.location?.longitude}")
Log.d("AbsensiApp", "Distance: ???")
// Or test manually
val distance = LocationValidator.calculateDistance(
-7.0, 110.4,
-7.0035, 110.4042
)
Log.d("Distance Test", "Result: $distance meters")
📸 Camera Issues
Issue 1: Camera Permission Denied
Error: "Izin kamera ditolak"
Solutions:
- Open Settings → Apps → YourApp → Permissions
- Find "Camera" permission
- Tap it → Select "Allow"
- Return to app and try again
For Emulator:
- Device Manager → Create/Edit device
- Verify "Camera" is checked in hardware
- Set Camera: "Emulated"
Issue 2: Photo Not Captured
Error: Camera opens but no photo saved
Causes & Solutions:
-
❌ User canceled camera intent → Just tap button again to retry
-
❌ Storage permission issue (older Android) → Grant
WRITE_EXTERNAL_STORAGEin Settings -
❌ Camera intent failure → Check if device has camera:
hasSystemFeature(PackageManager.FEATURE_CAMERA)
Debug:
// Check in logcat
Log.d("Camera", "Result Code: $resultCode")
Log.d("Camera", "Data: ${result.data}")
Log.d("Camera", "Bitmap: ${bitmap != null}")
Issue 3: Photo Preview Not Showing
Error: PhotoPreviewCard shows "Belum ada foto"
Causes & Solutions:
-
❌ Bitmap is null → Check Activity.RESULT_OK returned → Verify
result.data?.extras?.getParcelableworking -
❌ UI not updating → Ensure state.copy() is called → Check LaunchedEffect dependencies
Quick Fix:
// Add logging
if (bitmap != null) {
Log.d("Photo", "Bitmap size: ${bitmap.width}x${bitmap.height}")
state = state.copy(foto = bitmap)
} else {
Log.d("Photo", "Bitmap is null!")
}
🌐 Network Issues
Issue 1: Cannot Connect to Webhook
Error: "Gagal kirim ke server" or timeout
Causes & Solutions:
-
❌ No internet connection → Check WiFi/mobile data enabled → Ping google.com to verify
-
❌ Wrong webhook URL → Verify in
AttendanceConfig.kt→ Copy exact URL from N8n dashboard -
❌ Firewall/VPN blocking → Disable VPN temporarily → Check if institution firewall allows HTTPS
-
❌ N8n server down → Test with curl:
curl -X POST https://n8n.lab.ubharajaya.ac.id/webhook/...→ Check N8n status page
Webhook URL Check:
// File: AttendanceConfig.kt
const val WEBHOOK_PRODUCTION = "https://n8n.lab.ubharajaya.ac.id/webhook/23c6993d-1792-48fb-ad1c-ffc78a3e6254"
// ↑ Copy exact URL, no typos!
Issue 2: Server Returns 400/500 Error
Error: "Absensi ditolak server" with error code
Causes & Solutions:
-
Status 400: Data format wrong → Verify JSON structure matches expectations → Check all fields are present (npm, nama, lat, lon, foto) → Check foto is valid Base64
-
Status 401: Authentication failed → Add authentication token if required → Contact server admin
-
Status 500: Server error → Check N8n workflow logs → Verify database connection → Retry after server is fixed
Test with Test Webhook First:
// In MainActivity
isTest = true // Set this to test first
// Check results at: https://n8n.lab.ubharajaya.ac.id/webhook-test/...
Issue 3: Timeout (Takes Too Long)
Error: Request hangs for 30+ seconds then fails
Solutions:
- Check network speed: Test with speed.test.com
- Reduce photo size: Decrease
PHOTO_QUALITYinAttendanceConfig.kt - Increase timeout: Change
API_TIMEOUT_MS(but not recommended) - Check server response time (might be slow)
// File: AttendanceConfig.kt
const val PHOTO_QUALITY = 70 // Reduce from 80 to 70
const val API_TIMEOUT_MS = 30000 // Current 30 seconds
⚙️ Permission Issues
Issue 1: Permission Dialog Not Appearing
Error: App crashes or silently fails
Solutions:
-
Verify permission in
AndroidManifest.xml:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.INTERNET"/> -
Check permission launcher is called:
LaunchedEffect(Unit) { locationPermissionLauncher.launch( Manifest.permission.ACCESS_FINE_LOCATION ) } -
For Android 12+, check targetSdk:
targetSdk = 36
Issue 2: Permission Stuck on Denied
Error: User clicks "Deny" and can't recover
Solutions:
-
User must go to Settings manually: → Settings → Apps → YourApp → Permissions → Allow
-
Add UI hint in app:
if (state.errorMessage?.contains("Izin") == true) { Button("Buka Pengaturan") { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) startActivity(intent) } }
💾 Data & State Issues
Issue 1: Form Not Validating
Error: Submit button always disabled or always enabled
Causes & Solutions:
-
❌ Validation logic wrong → Check
LocationValidator.isLocationValid()implementation → Run unit tests:./gradlew test -
❌ State not updating → Verify state.copy() being called → Check all conditions in button enabled state
Validation Checklist:
// Button should be enabled only when ALL true:
val canSubmit = (
state.location != null && // Location acquired
state.foto != null && // Photo taken
state.validationResult.isValid && // Location valid
!state.isLoadingSubmit // Not currently submitting
)
SubmitButtonWithLoader(
// ...
isEnabled = canSubmit // ← Check this
)
Issue 2: State Lost on Screen Rotation
Error: Form data disappears when device rotates
Solutions:
This is normal - Compose restores state automatically via remember {}
To persist across full app restart:
// Would need ViewModel + SavedStateHandle (advanced feature)
// Currently: state is reset on process death (acceptable for MVP)
Issue 3: Photo Bitmap Memory Issue
Error: "OutOfMemoryError: Bitmap too large" or app crashes
Solutions:
-
Reduce photo quality:
const val PHOTO_QUALITY = 60 // Reduce from 80 -
Compress after capture:
// In bitmapToBase64: bitmap.compress(Bitmap.CompressFormat.JPEG, PHOTO_QUALITY, outputStream) -
For low-memory devices:
- Scale down bitmap before encoding
- Or use WebP format (better compression)
🧪 Testing Issues
Issue 1: Unit Tests Not Running
Error: "No tests found" or tests fail
Solutions:
-
Run from command line:
./gradlew test -
Or in Android Studio:
- Right-click test file → Run 'LocationValidatorTest'
- Or use Test Configuration
-
Verify test file location:
app/src/test/java/.../LocationValidatorTest.kt ✓ Correct app/src/androidTest/java/.../LocationValidatorTest.kt ✗ Wrong
Issue 2: Manual Testing Stuck
Error: Can't reproduce the flow
Quick Test Checklist:
- Device/emulator has internet
- Location services enabled
- All permissions granted
- Camera app works
- Webhook URL correct
Fastest Flow:
- Grant permissions automatically
- Hardcode valid location (for testing)
- Take photo
- Click submit
- Should complete in < 10 seconds
🎨 UI Issues
Issue 1: Layout Cut Off on Small Screens
Error: Buttons/text not visible
Solutions:
- Already implemented:
verticalScroll(rememberScrollState()) - Reduce padding:
modifier.padding(16.dp)→modifier.padding(8.dp) - Use
Columnscrolling instead of fixed height
Column(
modifier = modifier
.fillMaxSize()
.padding(16.dp)
.verticalScroll(rememberScrollState()) // ← This allows scrolling
)
Issue 2: Dark Mode Not Working
Error: Text hard to read or colors wrong
Solutions:
- Theme already supports dynamic colors (Material 3)
- Verify
Theme.kthas proper dark/light variants - Test: Settings → Display → Dark theme → On/Off
Issue 3: Loading Spinner Not Showing
Error: Button says "Mengirim..." but no spinner
Solutions:
-
Check
isLoadingstate is true:state = state.copy(isLoadingSubmit = true) -
Verify spinner code in
SubmitButtonWithLoader:if (isLoading) { CircularProgressIndicator(...) // ← Should show }
🆘 Emergency Troubleshooting
App Crashes on Launch
- Check Logcat for full error stack trace
- Look for line number where crash happens
- Most common: Missing permission or dependency
- Try:
Build→Clean Project→Rebuild Project
Complete State Reset
# Uninstall app completely
./gradlew uninstallDebug
# Clean all build artifacts
./gradlew clean
# Rebuild everything
./gradlew installDebug
Last Resort: Check Dependencies
// Verify all dependencies installed
./gradlew dependencies
// Check for conflicts
./gradlew dependencyInsight --dependency location-services
📊 Debugging Commands
View All Logs
adb logcat
Filter Specific Tags
adb logcat | grep "AbsensiApp"
adb logcat | grep "N8nService"
adb logcat | grep "LocationValidator"
Clear Logs
adb logcat -c
Install Debug App
./gradlew installDebug
Uninstall App
adb uninstall id.ac.ubharajaya.sistemakademik
View App Logs in Real-time
adb logcat *:S AbsensiApp:D
🔍 Verification Checklist
Before declaring bug fixed, verify:
- Issue is reproducible
- Logs show no errors
- All permissions granted
- Internet connection active
- Coordinates correct
- Server responding
- Photo quality acceptable
- Location accuracy good
- Form submission successful
- Data received at webhook
Still stuck?
- Re-read QUICK_REFERENCE.md
- Check DOKUMENTASI.md for detailed explanations
- Review TESTING_CHECKLIST.md for testing procedures
- Check ARCHITECTURE.md for design understanding
- Look at comments in actual code files
Last Updated: January 14, 2026
Version: 1.0