name: F6.1 apply Android lint fix on: push: branches: - 'release/f6-1-presentation-ready' paths: - '.github/workflows/f6-1-apply-android-lint-fix.yml' permissions: contents: write jobs: apply: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: release/f6-1-presentation-ready fetch-depth: 0 - name: Apply targeted runtime fixes run: | python - <<'PY' from pathlib import Path replacements = { 'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/DhApp.kt': ( ''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''', ''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n try {\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''', ), 'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/F3VisitRoot.kt': ( ''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) {\n continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n } else {\n continuation.resume(F3GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''', ''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n try {\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) {\n continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n } else {\n continuation.resume(F3GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''', ), 'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/FieldFindingScreen.kt': ( ''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(FindingGeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''', ''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n try {\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(FindingGeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''', ), 'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/MobileActsScreen.kt': ( ''' val source = CancellationTokenSource()\n LocationServices.getFusedLocationProviderClient(context)\n .getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("Ubicación no disponible"))\n else continuation.resume(ActSignatureGeo(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''', ''' val source = CancellationTokenSource()\n try {\n LocationServices.getFusedLocationProviderClient(context)\n .getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("Ubicación no disponible"))\n else continuation.resume(ActSignatureGeo(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''', ), } for file_name, (before, after) in replacements.items(): path = Path(file_name) text = path.read_text() count = text.count(before) if count != 1: raise SystemExit(f'{file_name}: expected exactly one GPS block, found {count}') path.write_text(text.replace(before, after)) manifest = Path('android-app/app/src/main/AndroidManifest.xml') text = manifest.read_text() before = ' \n' after = before + ' \n' if text.count(before) != 1: raise SystemExit('AndroidManifest.xml: CAMERA permission block not unique') if 'android.hardware.camera' not in text: manifest.write_text(text.replace(before, after)) # Self-delete so this one-shot workflow never ships in the release branch. Path('.github/workflows/f6-1-apply-android-lint-fix.yml').unlink() PY - name: Commit targeted fixes run: | set -Eeuo pipefail git config user.name 'github-actions[bot]' git config user.email '41898282+github-actions[bot]@users.noreply.github.com' git add android-app .github/workflows/f6-1-apply-android-lint-fix.yml git diff --cached --check git commit -m 'fix(android): handle location permission races and optional camera' git push origin HEAD:release/f6-1-presentation-ready