585 lines
14 KiB
Markdown
585 lines
14 KiB
Markdown
# 🔧 Troubleshooting Guide
|
|
|
|
## Common Issues & Solutions
|
|
|
|
### 🚨 Build Issues
|
|
|
|
#### Issue 1: Gradle Sync Failed
|
|
**Error**: `Gradle sync failed: Failed to resolve...`
|
|
|
|
**Solutions**:
|
|
1. Clean project: `Build` → `Clean Project`
|
|
2. Invalidate caches: `File` → `Invalidate Caches / Restart`
|
|
3. Sync Gradle: `File` → `Sync Now`
|
|
4. Delete `.gradle` folder and try again
|
|
5. Check internet connection (gradle downloads dependencies)
|
|
|
|
---
|
|
|
|
#### Issue 2: Missing Dependencies
|
|
**Error**: `Unresolved reference: LocationValidator` atau similar
|
|
|
|
**Solutions**:
|
|
1. Verify file exists in correct package structure
|
|
2. Check package declaration at top of file
|
|
3. Rebuild project: `Build` → `Rebuild Project`
|
|
4. Check that all imports are present
|
|
5. File → Invalidate Caches / Restart
|
|
|
|
---
|
|
|
|
#### Issue 3: Kotlin Syntax Error
|
|
**Error**: `Type mismatch: inferred type is String? but Boolean was expected`
|
|
|
|
**Solutions**:
|
|
1. Check null-safety: use `?.` or `!!` appropriately
|
|
2. Verify data class properties match their types
|
|
3. Look for missing type annotations
|
|
4. 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**:
|
|
```kotlin
|
|
// 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_LATITUDE` dan `REFERENCE_LONGITUDE` di `AttendanceConfig.kt`
|
|
→ Verify with Google Maps
|
|
|
|
- ❌ Radius too small
|
|
→ Increase `ALLOWED_RADIUS_METERS` temporarily 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**:
|
|
```kotlin
|
|
// 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**:
|
|
1. Go to campus location with device
|
|
2. Open Google Maps
|
|
3. Long-click on map → Coordinates appear at top
|
|
4. Copy latitude and longitude
|
|
5. 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**:
|
|
```kotlin
|
|
// 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**:
|
|
1. Open Settings → Apps → YourApp → Permissions
|
|
2. Find "Camera" permission
|
|
3. Tap it → Select "Allow"
|
|
4. Return to app and try again
|
|
|
|
**For Emulator**:
|
|
1. Device Manager → Create/Edit device
|
|
2. Verify "Camera" is checked in hardware
|
|
3. 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_STORAGE` in Settings
|
|
|
|
- ❌ Camera intent failure
|
|
→ Check if device has camera: `hasSystemFeature(PackageManager.FEATURE_CAMERA)`
|
|
|
|
**Debug**:
|
|
```kotlin
|
|
// 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?.getParcelable` working
|
|
|
|
- ❌ UI not updating
|
|
→ Ensure state.copy() is called
|
|
→ Check LaunchedEffect dependencies
|
|
|
|
**Quick Fix**:
|
|
```kotlin
|
|
// 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**:
|
|
```kotlin
|
|
// 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**:
|
|
```kotlin
|
|
// 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**:
|
|
1. Check network speed: Test with speed.test.com
|
|
2. Reduce photo size: Decrease `PHOTO_QUALITY` in `AttendanceConfig.kt`
|
|
3. Increase timeout: Change `API_TIMEOUT_MS` (but not recommended)
|
|
4. Check server response time (might be slow)
|
|
|
|
```kotlin
|
|
// 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**:
|
|
1. Verify permission in `AndroidManifest.xml`:
|
|
```xml
|
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
|
<uses-permission android:name="android.permission.CAMERA"/>
|
|
<uses-permission android:name="android.permission.INTERNET"/>
|
|
```
|
|
|
|
2. Check permission launcher is called:
|
|
```kotlin
|
|
LaunchedEffect(Unit) {
|
|
locationPermissionLauncher.launch(
|
|
Manifest.permission.ACCESS_FINE_LOCATION
|
|
)
|
|
}
|
|
```
|
|
|
|
3. For Android 12+, check targetSdk:
|
|
```gradle
|
|
targetSdk = 36
|
|
```
|
|
|
|
---
|
|
|
|
#### Issue 2: Permission Stuck on Denied
|
|
**Error**: User clicks "Deny" and can't recover
|
|
|
|
**Solutions**:
|
|
1. User must go to Settings manually:
|
|
→ Settings → Apps → YourApp → Permissions → Allow
|
|
|
|
2. Add UI hint in app:
|
|
```kotlin
|
|
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**:
|
|
```kotlin
|
|
// 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:
|
|
```kotlin
|
|
// 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**:
|
|
1. Reduce photo quality:
|
|
```kotlin
|
|
const val PHOTO_QUALITY = 60 // Reduce from 80
|
|
```
|
|
|
|
2. Compress after capture:
|
|
```kotlin
|
|
// In bitmapToBase64:
|
|
bitmap.compress(Bitmap.CompressFormat.JPEG, PHOTO_QUALITY, outputStream)
|
|
```
|
|
|
|
3. 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**:
|
|
1. Run from command line:
|
|
```bash
|
|
./gradlew test
|
|
```
|
|
|
|
2. Or in Android Studio:
|
|
- Right-click test file → Run 'LocationValidatorTest'
|
|
- Or use Test Configuration
|
|
|
|
3. 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**:
|
|
1. Grant permissions automatically
|
|
2. Hardcode valid location (for testing)
|
|
3. Take photo
|
|
4. Click submit
|
|
5. Should complete in < 10 seconds
|
|
|
|
---
|
|
|
|
### 🎨 UI Issues
|
|
|
|
#### Issue 1: Layout Cut Off on Small Screens
|
|
**Error**: Buttons/text not visible
|
|
|
|
**Solutions**:
|
|
1. Already implemented: `verticalScroll(rememberScrollState())`
|
|
2. Reduce padding: `modifier.padding(16.dp)` → `modifier.padding(8.dp)`
|
|
3. Use `Column` scrolling instead of fixed height
|
|
|
|
```kotlin
|
|
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**:
|
|
1. Theme already supports dynamic colors (Material 3)
|
|
2. Verify `Theme.kt` has proper dark/light variants
|
|
3. Test: Settings → Display → Dark theme → On/Off
|
|
|
|
---
|
|
|
|
#### Issue 3: Loading Spinner Not Showing
|
|
**Error**: Button says "Mengirim..." but no spinner
|
|
|
|
**Solutions**:
|
|
1. Check `isLoading` state is true:
|
|
```kotlin
|
|
state = state.copy(isLoadingSubmit = true)
|
|
```
|
|
|
|
2. Verify spinner code in `SubmitButtonWithLoader`:
|
|
```kotlin
|
|
if (isLoading) {
|
|
CircularProgressIndicator(...) // ← Should show
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🆘 Emergency Troubleshooting
|
|
|
|
### App Crashes on Launch
|
|
1. Check Logcat for full error stack trace
|
|
2. Look for line number where crash happens
|
|
3. Most common: Missing permission or dependency
|
|
4. Try: `Build` → `Clean Project` → `Rebuild Project`
|
|
|
|
### Complete State Reset
|
|
```bash
|
|
# Uninstall app completely
|
|
./gradlew uninstallDebug
|
|
|
|
# Clean all build artifacts
|
|
./gradlew clean
|
|
|
|
# Rebuild everything
|
|
./gradlew installDebug
|
|
```
|
|
|
|
### Last Resort: Check Dependencies
|
|
```gradle
|
|
// Verify all dependencies installed
|
|
./gradlew dependencies
|
|
|
|
// Check for conflicts
|
|
./gradlew dependencyInsight --dependency location-services
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Debugging Commands
|
|
|
|
### View All Logs
|
|
```bash
|
|
adb logcat
|
|
```
|
|
|
|
### Filter Specific Tags
|
|
```bash
|
|
adb logcat | grep "AbsensiApp"
|
|
adb logcat | grep "N8nService"
|
|
adb logcat | grep "LocationValidator"
|
|
```
|
|
|
|
### Clear Logs
|
|
```bash
|
|
adb logcat -c
|
|
```
|
|
|
|
### Install Debug App
|
|
```bash
|
|
./gradlew installDebug
|
|
```
|
|
|
|
### Uninstall App
|
|
```bash
|
|
adb uninstall id.ac.ubharajaya.sistemakademik
|
|
```
|
|
|
|
### View App Logs in Real-time
|
|
```bash
|
|
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?**
|
|
1. Re-read QUICK_REFERENCE.md
|
|
2. Check DOKUMENTASI.md for detailed explanations
|
|
3. Review TESTING_CHECKLIST.md for testing procedures
|
|
4. Check ARCHITECTURE.md for design understanding
|
|
5. Look at comments in actual code files
|
|
|
|
---
|
|
|
|
**Last Updated**: January 14, 2026
|
|
**Version**: 1.0
|
|
|