====== Preguntas Frecuentes (FAQ) ======
===== Instalación y Setup =====
==== P: ¿Cómo configuro mi máquina para desarrollar en PAE? ====
**R**: Sigue [[environment-setup|environment-setup.md]]. Necesitas:
* JDK 17
* Android Studio
* Android SDK 27-36
* Git
./gradlew clean build # verificar setup
==== P: ¿Qué versión de Android Studio necesito? ====
**R**: Android Studio 2023.2 o superior. Si es vieja, actualiza:
* macOS: ''brew install android-studio''
* Windows: descarga de developer.android.com
----
===== Compilación y Deployment =====
==== P: ¿Cómo compilo para release? ====
**R**: Ver [[build-and-deployment|build-and-deployment.md]]:
export KEYSTORE_PASS="tu_pass"
export KEY_PASS="tu_key_pass"
./gradlew :Machine:assembleRelease
# APK está en: build/outputs/apk/release/Machine-release.apk
==== P: ¿Por qué dice "Signing config not defined"? ====
**R**: Necesitas configurar signing en ''build.gradle.kts'':
android {
signingConfigs {
release {
storeFile = file("../keystore/machine-release.jks")
storePassword = System.getenv("KEYSTORE_PASS")
keyAlias = "machine-key"
keyPassword = System.getenv("KEY_PASS")
}
}
buildTypes {
release {
signingConfig = signingConfigs["release"]
}
}
}
==== P: ¿Cómo instalo en dispositivo? ====
**R**:
# Debug
adb install build/outputs/apk/debug/Machine-debug.apk
# Release (después de release build)
adb install build/outputs/apk/release/Machine-release.apk
# Reinstalar
adb install -r build/outputs/apk/release/Machine-release.apk
----
===== Desarrollo =====
==== P: ¿Cómo agrego un nuevo módulo? ====
**R**:
- Crear carpeta: ''mkdir MyModule''
- Crear ''MyModule/build.gradle.kts'':
plugins {
id("com.android.library")
kotlin("android")
}
android {
namespace = "co.ada.mymodule"
compileSdk = 36
// ...
}
dependencies {
implementation(project(":Contract"))
implementation(project(":Core"))
}
- Agregar a ''settings.gradle.kts'':
include(":MyModule")
project(":MyModule").projectDir = file("MyModule")
- Sync: ''> Gradle → Sync Now''
==== P: ¿Cómo agrego código a Machine vs RutaPAE? ====
**R**:
* Machine (app Android): ''Machine/src/main/...''
* RutaPAE (app Android): ''RutaPAE/src/main/...''
* Lógica compartida: ''MachineDomain/'' o ''RutaPAEDomain/''
* Datos: ''MachineData/'' o ''RutaPAEData/''
==== P: ¿Dónde agrego un nuevo estado? ====
**R**: En ''MachineDomain/src/.../state/'':
- Crear ''MyNewState.kt''
- Implementar interfaz ''State''
- Agregar a lista en ''StateManager.workflow''
Ver [[developer-guide#agregar//nuevo//estado//a//machine|developer-guide.md]].
==== P: ¿Cómo agrego un endpoint HTTP? ====
**R**: En ''MachineDomain/src/.../endpoints/MachineEndpoints.kt'':
object MachineEndpoints {
fun getMyData(): Request {
return "${backendUrl}/api/mydata"
.httpGet()
.responseJson()
}
}
// Usar
val data = MachineEndpoints.getMyData()
----
===== Testing =====
==== P: ¿Cómo escribo tests? ====
**R**: Ver [[testing-guide|testing-guide.md]]. Ejemplo unit test:
@RunWith(RobolectricTestRunner::class)
class StateManagerTest {
@Test
fun testStateTransition() {
val manager = StateManager(mockRepository, mockHardware)
manager.init(context)
assertEquals("WaitingForWeight", manager.getCurrentState())
}
}
Ejecutar:
./gradlew test
==== P: ¿Cómo hago UI tests? ====
**R**: Con Compose test:
@RunWith(AndroidJUnit4::class)
class LaboratoryScreenTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun testButtonVisible() {
composeTestRule.setContent {
LaboratoryScreen(viewModel())
}
composeTestRule.onNodeWithText("Capturar").assertIsDisplayed()
}
}
----
===== P2P y Sincronización =====
==== P: ¿Cómo sincronizo entregas entre Machine y RutaPAE? ====
**R**: En RutaPAE, enqueue worker:
DeliverySyncScheduler.enqueue(context, machineId = 1L)
Esto crea una operación P2P que:
- Descubre máquina
- Conecta vía Wi-Fi Direct
- Descarga entregas
- Guarda localmente
Ver [[p2p-flow|p2p-flow.md]].
==== P: ¿Qué pasa si P2P falla? ====
**R**: Hay fallback a hotspot. Si también falla:
* Retry automático con backoff exponencial
* Notificación al usuario
* Peut être reintentado manualmente
==== P: ¿Cómo sé si máquina está disponible? ====
**R**:
val p2pManager = DomainManager.p2pManager
val peers = p2pManager.discoveredMachineCandidates()
// peers contiene máquinas disponibles
----
===== Hardware =====
==== P: ¿Cómo capturo foto en Machine? ====
**R**: En StateManager:
val bitmap = Camera2Service(context).takeSelfie()
if (bitmap != null) {
stateData.facePhoto = bitmap
next()
} else {
retry("Camera error")
}
Ver [[hardware-integration|hardware-integration.md]].
==== P: ¿Cómo conecto a balanza Bluetooth? ====
**R**: Requiere permissions:
Luego:
val scale = BluetoothScaleManager(context, MAC_ADDRESS)
scale.addEventListener(listener)
val weight = scale.getWeight()
==== P: ¿Cómo obtengo MAC address de balanza? ====
**R**: En dispositivo Android:
adb shell bluetoothctl
> devices
# lista con direcciones MAC
----
===== Database y Persistencia =====
==== P: ¿Cómo hago query vectorial a beneficiarios? ====
**R**: Usar ''findSimilar'':
val embedding = generateEmbedding(facePhoto)
val similar = BeneficiaryService.findSimilar(embedding, similarity = 0.8f)
// similar contiene top resultados por similitud
for (beneficiary in similar) {
println("${beneficiary.name}: ${similarity}")
}
==== P: ¿Cómo agrego nuevo campo a Delivery? ====
**R**:
- Editar ''data class Delivery'' en ''MachineData/src/.../entities/''
- El ORM migrará automáticamente (open-source VectorDB)
data class Delivery(
val id: Long,
val weight: Float,
val nuevoCampo: String = "" // new field
)
==== P: ¿Cómo elimino datos viejos? ====
**R**: Limpiar en background:
val oneMonthAgo = System.currentTimeMillis() - 30.days.inMilliseconds
repository.deliveries.where { it.createdAt < oneMonthAgo }
.forEach { repository.deliveries.delete(it.id) }
----
===== Seguridad =====
==== P: ¿Cómo protejo credenciales sensitivas? ====
**R**: Ver [[security-and-privacy|security-and-privacy.md]]:
// Guardar tokens de forma segura
val encryptedPrefs = EncryptedSharedPreferences.create(context, ...)
encryptedPrefs.edit().putString("api_token", token).apply()
// Leer
val token = encryptedPrefs.getString("api_token", null)
==== P: ¿Cómo evito logging de datos sensibles? ====
**R**: Usar logger personalizado:
// ❌ Malo
Log.d("API", "Token: $token")
// ✅ Bueno
Log.d("API", "User authenticated")
// ✅ Mejor
SecureLogger.logToken(token) // oculta la mayoría
==== P: ¿Mi app necesita certificate pinning? ====
**R**: Sí, en producción. Configurar:
val certificatePinner = CertificatePinner.Builder()
.add("backend.api.com", "sha256/AAAAAAA...")
.build()
----
===== Performance =====
==== P: ¿Cómo optimizo búsqueda de beneficiarios? ====
**R**: Este es el cuello de botella típico. Soluciones:
- Índice vectorial HNSW (ya implementado in VectorDB)
- Caché de resultados recientes
- Limitar K nearest neighbors
==== P: ¿Por qué la app está lenta? ====
**R**: Causas comunes:
- Operación BD en main thread - usar ''withContext(Dispatchers.IO)''
- Búsqueda sobre muchas fotos - implementar paginación
- Memory leak - usar Android Profiler
- Compilación debug - agregar -Xmx4g en gradle.properties
Ver [[troubleshooting|troubleshooting.md#app-muy-lenta]].
----
===== Debugging =====
==== P: ¿Cómo veo logs en Android Studio? ====
**R**: Abrir Logcat:
Android Studio → View → Tool Windows → Logcat
Filtrar:
Filter: "StateManager" # solo líneas con StateManager
O vía terminal:
adb logcat | grep StateManager
==== P: ¿Cómo seteo breakpoint? ====
**R**: Click en número de línea (izquierda):
1. Marcar breakpoint (punto rojo)
2. Run → Debug 'Machine' (Shift+F9)
3. App pausa cuando llega a línea
4. F9 para continuar
5. F10 para step over
6. F11 para step into
==== P: App crashea pero sin error visible? ====
**R**: Ver logcat completo:
adb logcat | tail -100
O usar Android Profiler:
Android Studio → View → Tool Windows → Profiler
----
===== Errores comunes =====
==== P: "Gradle sync failed" ====
**R**:
./gradlew clean
rm -rf .gradle
./gradlew build
Ver [[troubleshooting|troubleshooting.md#gradle-sync-failed]].
==== P: "No such key: deliveries" ====
**R**: No synchronized gradle. Falta ''sync'':
Android Studio: File → Sync Now
O terminal:
./gradlew clean build
==== P: "Camera permission denied" ====
**R**: Solicitar en runtime (API 23+):
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), 100)
}
==== P: "Bluetooth connection failed" ====
**R**:
- Verificar device está pareado
- Permisos solicitados
- MAC address correcto
- Dispositivo no está dormido
Ver [[troubleshooting|troubleshooting.md#bluetooth-connection-refused]].
----
===== Mejores prácticas =====
==== P: ¿Cuándo usar Flow vs StateFlow? ====
**R**:
* **Flow**: observable, emite datos secuencialmente, puede estar vacío
* **StateFlow**: siempre tiene estado actual, último valor disponible
// Flow: observable stream
val deliveriesFlow: Flow> = ...
// StateFlow: estado actual
val currentState = MutableStateFlow(State.Idle)
currentState.value // acceder estado actual
==== P: ¿Cuándo usar suspend vs async? ====
**R**:
* **suspend**: función que puede pausarse (estructura lineal)
* **async**: inicia tarea concurrent (necesita .await())
// suspend: simple
suspend fun getData(): String = repository.getString()
// async: paralelismo
val result1 = async { repository.getString() }
val result2 = async { repository.getInt() }
// esperar ambas
val r1 = result1.await()
val r2 = result2.await()
==== P: ¿Cómo organizo código nuevo? ====
**R**: Seguir estructura:
co.ada.MODULE/
├── screens/ # UI Composables (si app)
├── viewmodels/ # ViewModel (si app)
├── domain/
│ ├── managers/ # orquestadores
│ └── services/ # lógica
├── data/
│ ├── entities/ # modelos BD
│ └── repository/ # CRUD
└── di/ # inyección
----
===== Contribución =====
==== P: ¿Cómo contribuyo code? ====
**R**:
- Fork / branch
- Hacer cambios + tests
- Push
- Pull request con descripción
Ver [[best-practices|best-practices.md#version-control]].
==== P: ¿Qué estándares de código? ====
**R**: Ver [[best-practices|best-practices.md]]:
* Usar nombres descriptivos
* One responsibility per class/function
* Test coverage mínimo 70%
* Sin hardcoded secrets
* Lint checks pasan
----
===== Contacto y Soporte =====
==== P: ¿Dónde reporto bugs? ====
**R**: Issues en GitHub con:
* Descripción clara
* Steps para reproducir
* Log/stack trace
* Ambiente (dispositivo, OS, versión app)
==== P: ¿Dónde veo roadmap? ====
**R**: Proyectos en GitHub. Planificación trimestral.
==== P: ¿Cómo pido una feature? ====
**R**: Abrir discussion en GitHub con:
* Caso de uso
* Por qué es importante
* Impacto estimado
----
===== Recursos =====
* [[start|Documentación completa]]
* [[adr-architecture-decisions|Architecture Decision Records]]
* [[best-practices|Best practices]]
* [[troubleshooting|Troubleshooting]]
* [[glossary|Glosario]]
* [[api-reference|API Reference]]