xonecode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +167 -0
  2. package/dist/agent/authEnDisco.js +56 -0
  3. package/dist/agent/authEnDisco.js.map +1 -0
  4. package/dist/agent/configEnDisco.js +109 -0
  5. package/dist/agent/configEnDisco.js.map +1 -0
  6. package/dist/agent/crearProyecto.js +34 -0
  7. package/dist/agent/crearProyecto.js.map +1 -0
  8. package/dist/agent/entorno.js +124 -0
  9. package/dist/agent/entorno.js.map +1 -0
  10. package/dist/agent/guionizado.js +53 -0
  11. package/dist/agent/guionizado.js.map +1 -0
  12. package/dist/agent/instantanea.js +129 -0
  13. package/dist/agent/instantanea.js.map +1 -0
  14. package/dist/agent/interrupts.js +83 -0
  15. package/dist/agent/interrupts.js.map +1 -0
  16. package/dist/agent/mensajes.js +24 -0
  17. package/dist/agent/mensajes.js.map +1 -0
  18. package/dist/agent/modelos.js +42 -0
  19. package/dist/agent/modelos.js.map +1 -0
  20. package/dist/agent/normalizar.js +20 -0
  21. package/dist/agent/normalizar.js.map +1 -0
  22. package/dist/agent/perfiles.js +103 -0
  23. package/dist/agent/perfiles.js.map +1 -0
  24. package/dist/agent/proyecto.js +107 -0
  25. package/dist/agent/proyecto.js.map +1 -0
  26. package/dist/agent/puente.js +142 -0
  27. package/dist/agent/puente.js.map +1 -0
  28. package/dist/agent/resumenDeTool.js +48 -0
  29. package/dist/agent/resumenDeTool.js.map +1 -0
  30. package/dist/agent/skills.js +65 -0
  31. package/dist/agent/skills.js.map +1 -0
  32. package/dist/agent/textoDeTool.js +113 -0
  33. package/dist/agent/textoDeTool.js.map +1 -0
  34. package/dist/agent/turnoReal.js +173 -0
  35. package/dist/agent/turnoReal.js.map +1 -0
  36. package/dist/agent/verificador.js +65 -0
  37. package/dist/agent/verificador.js.map +1 -0
  38. package/dist/agent/xoneAgent.js +106 -0
  39. package/dist/agent/xoneAgent.js.map +1 -0
  40. package/dist/bin.js +9 -0
  41. package/dist/bin.js.map +1 -0
  42. package/dist/cli/aprobar.js +82 -0
  43. package/dist/cli/aprobar.js.map +1 -0
  44. package/dist/cli/config.js +133 -0
  45. package/dist/cli/config.js.map +1 -0
  46. package/dist/cli/consola.js +346 -0
  47. package/dist/cli/consola.js.map +1 -0
  48. package/dist/cli/describe.js +53 -0
  49. package/dist/cli/describe.js.map +1 -0
  50. package/dist/cli/doctor.js +45 -0
  51. package/dist/cli/doctor.js.map +1 -0
  52. package/dist/cli/main.js +520 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/cli/markdown.js +90 -0
  55. package/dist/cli/markdown.js.map +1 -0
  56. package/dist/cli/run.js +164 -0
  57. package/dist/cli/run.js.map +1 -0
  58. package/dist/cli/spinner.js +89 -0
  59. package/dist/cli/spinner.js.map +1 -0
  60. package/dist/cli/stdio.js +193 -0
  61. package/dist/cli/stdio.js.map +1 -0
  62. package/dist/cli/tema.js +40 -0
  63. package/dist/cli/tema.js.map +1 -0
  64. package/dist/cli/verify.js +62 -0
  65. package/dist/cli/verify.js.map +1 -0
  66. package/dist/core/bitacora.js +30 -0
  67. package/dist/core/bitacora.js.map +1 -0
  68. package/dist/core/config.js +236 -0
  69. package/dist/core/config.js.map +1 -0
  70. package/dist/core/contextos.js +56 -0
  71. package/dist/core/contextos.js.map +1 -0
  72. package/dist/core/deps.js +51 -0
  73. package/dist/core/deps.js.map +1 -0
  74. package/dist/core/diff.js +79 -0
  75. package/dist/core/diff.js.map +1 -0
  76. package/dist/core/esqueleto.js +366 -0
  77. package/dist/core/esqueleto.js.map +1 -0
  78. package/dist/core/events.js +2 -0
  79. package/dist/core/events.js.map +1 -0
  80. package/dist/core/modelos.js +85 -0
  81. package/dist/core/modelos.js.map +1 -0
  82. package/dist/core/notify.js +106 -0
  83. package/dist/core/notify.js.map +1 -0
  84. package/dist/core/ports.js +114 -0
  85. package/dist/core/ports.js.map +1 -0
  86. package/dist/core/turno.js +127 -0
  87. package/dist/core/turno.js.map +1 -0
  88. package/dist/vendor/hitl.js +103 -0
  89. package/dist/vendor/hitl.js.map +1 -0
  90. package/dist/vendor/skillLoaders/catalog.js +74 -0
  91. package/dist/vendor/skillLoaders/catalog.js.map +1 -0
  92. package/dist/vendor/tokenTracking.js +58 -0
  93. package/dist/vendor/tokenTracking.js.map +1 -0
  94. package/package.json +41 -0
  95. package/skills/xone-debugging/SKILL.md +52 -0
  96. package/skills/xone-debugging/references/faq.md +561 -0
  97. package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
  98. package/skills/xone-development/SKILL.md +105 -0
  99. package/skills/xone-development/references/anti-patrones.md +94 -0
  100. package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
  101. package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
  102. package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
  103. package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
  104. package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
  105. package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
  106. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
  107. package/skills/xone-development/references/datos/appdata.md +611 -0
  108. package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
  109. package/skills/xone-development/references/datos/http.md +366 -0
  110. package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
  111. package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
  112. package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
  113. package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
  114. package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
  115. package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
  116. package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
  117. package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
  118. package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
  119. package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
  120. package/skills/xone-development/references/indice-completo.md +77 -0
  121. package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
  122. package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
  123. package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
  124. package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
  125. package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
  126. package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
  127. package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
  128. package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
  129. package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
  130. package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
  131. package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
  132. package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
  133. package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
  134. package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
  135. package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
  136. package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
  137. package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
  138. package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
  139. package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
  140. package/skills/xone-development/references/tipos-de-prop.md +28 -0
  141. package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
  142. package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
  143. package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
  144. package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
  145. package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
  146. package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
  147. package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
  148. package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
  149. package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
  150. package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
  151. package/skills/xone-development/references/xml-ui/mapas.md +685 -0
  152. package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
  153. package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
  154. package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
  155. package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
  156. package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
  157. package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
  158. package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
  159. package/skills/xone-plan-builder/SKILL.md +183 -0
  160. package/skills/xone-plan-builder/agents/openai.yaml +5 -0
  161. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
  162. package/skills/xone-project-generator/SKILL.md +111 -0
  163. package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
  164. package/skills/xone-project-generator/references/colecciones-base.md +16 -0
  165. package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
  166. package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
  167. package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
  168. package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
  169. package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
  170. package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
  171. package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
  172. package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
  173. package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
  174. package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
  175. package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
  176. package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
  177. package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
  178. package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
  179. package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
  180. package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
  181. package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
  182. package/skills/xone-review/SKILL.md +189 -0
  183. package/skills/xone-spec-builder/SKILL.md +235 -0
  184. package/skills/xone-spec-builder/agents/openai.yaml +5 -0
  185. package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
  186. package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
  187. package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
@@ -0,0 +1,506 @@
1
+ # XOne JavaScript — ui: GPS, cámara, firma, escáner y timers
2
+
3
+ > Fuente: `xone/v2/xone-help-docs/topics/03b-js-ui.md` §3.5–§3.9. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
4
+
5
+ Contenido: §3.5 GPS (startGps completo, GpsCollection, GpsTools) y cámara/archivos (startCamera, scanDocument, recognizeText) · §3.6 firma digital · §3.7 escáner QR/barcode · §3.8 sleep y timers · §3.9 otros
6
+
7
+ ---
8
+
9
+ ### 3.5 GPS
10
+
11
+ ```javascript
12
+ // === Iniciar GPS (modo básico) ===
13
+ ui.startGps();
14
+
15
+ // === Iniciar GPS con configuración completa ===
16
+ ui.startGps({
17
+ nodeName : "callbackgps", // Handler en la coll que recibe las actualizaciones
18
+ timeBetweenUpdates : 10000, // Milisegundos entre actualizaciones
19
+ minimumMetersDistanceRange: 10, // Metros minimos de desplazamiento para notificar
20
+ maxUpdateDelayMillis : 0,
21
+ priority : "high", // high / balanced / low_power / passive
22
+ maxUpdates : 1000, // Máximo de actualizaciones
23
+ durationMs : 3600000, // Duracion total del servicio (1 hora)
24
+ granularity : "permission_level", // permission_level / fine / coarse
25
+ waitForAccurateLocation : true
26
+ });
27
+
28
+ // === Detener GPS ===
29
+ ui.stopGps();
30
+
31
+ // === Comprobar estado del GPS ===
32
+ let nStatus = ui.checkGpsStatus();
33
+ // 0: No hay hardware GPS
34
+ // 1: Solo GPS activado
35
+ // 2: Solo WiFi/redes activado
36
+ // 3: Ninguno activado (pedir permiso)
37
+ // 4: GPS y WiFi/redes activados (optimo)
38
+
39
+ // === Pedir permiso de GPS al usuario ===
40
+ ui.askUserForGpsPermission({
41
+ onEnabled: function() {
42
+ ui.showToast("GPS activado correctamente");
43
+ },
44
+ onDenied: function() {
45
+ ui.showToast("Se necesita GPS para esta funcion");
46
+ }
47
+ });
48
+
49
+ // === GpsCollection - Leer posicion actual del GPS ===
50
+ // Convencion: el proyecto declara una coll llamada "GpsCollection" con connector GPS.
51
+ // NO es una coll built-in del framework — hay que declararla en el mapping del proyecto.
52
+ // El patron correcto es loadAll() + get(0), NO startBrowse/endBrowse.
53
+ function actualizarGps() {
54
+ let collGps = appData.getCollection("GpsCollection");
55
+ collGps.loadAll();
56
+ let objGps = collGps.get(0);
57
+
58
+ if (!objGps) return false; // GPS no disponible
59
+ if (objGps.STATUS != 1) return false; // Sin señal GPS
60
+ if (!objGps.LONGITUD) return false; // Sin cobertura GPS
61
+
62
+ self.MAP_FAKE = objGps.FAKE; // 1 = localización simulada (mock)
63
+ self.MAP_LONGITUD = objGps.LONGITUD;
64
+ self.MAP_LATITUD = objGps.LATITUD;
65
+ self.MAP_ALTITUD = objGps.ALTITUD;
66
+ self.MAP_VELOCIDAD = objGps.VELOCIDAD;
67
+ self.MAP_RUMBO = objGps.RUMBO;
68
+ self.MAP_FGPS = objGps.FGPS; // Fecha GPS
69
+ self.MAP_HGPS = objGps.HGPS; // Hora GPS
70
+ self.MAP_STATUS = objGps.STATUS;
71
+ self.MAP_SATELITES = objGps.SATELITES;
72
+ self.MAP_FUENTE = objGps.FUENTE; // Proveedor: gps, network, etc.
73
+ self.MAP_PRECISION = objGps.PRECISION;
74
+
75
+ ui.refreshValue("MAP_LONGITUD", "MAP_LATITUD", "MAP_ALTITUD",
76
+ "MAP_VELOCIDAD", "MAP_RUMBO", "MAP_STATUS",
77
+ "MAP_SATELITES", "MAP_FUENTE", "MAP_PRECISION");
78
+ return true;
79
+ }
80
+
81
+ // === GpsTools - Utilidades de geolocalizacion ===
82
+
83
+ // Distancia entre dos puntos (metros)
84
+ let nMetros = new GpsTools().distanceTo([
85
+ { latitude: 38.8685452, longitude: -6.8170906 },
86
+ { latitude: 40.4167747, longitude: -3.70379019 }
87
+ ]);
88
+
89
+ // Distancia entre dos puntos (alternativa con dos objetos)
90
+ let nMetros2 = new GpsTools().distanceBetweenCoordinates(
91
+ { latitude: 38.87, longitude: -6.97 },
92
+ { latitude: 40.42, longitude: -3.70 }
93
+ );
94
+
95
+ // Geocodificacion inversa: coordenadas -> dirección
96
+ let result = new GpsTools().getAddressFromPosition("38.8862106, -7.0040345");
97
+ // result: { locality, subLocality, adminArea, subAdminArea, features,
98
+ // country, countryCode, street, number, address, postal }
99
+
100
+ // Geocodificacion directa: dirección -> coordenadas
101
+ let pos = new GpsTools().getPositionFromAddress("Badajoz");
102
+ // pos: { latitude, longitude } o null si no se encuentra
103
+
104
+ // Verificar si un punto esta dentro de un poligono
105
+ let bDentro = new GpsTools().containsLocation(
106
+ "40.3633442, -1.0893794", // Punto a verificar
107
+ ["38.8685452, -6.8170906", "40.4167747, -3.70379019", "41.3850632, 2.1734035"]
108
+ );
109
+
110
+ // Ultima posicion conocida
111
+ let location = new GpsTools().getLastKnownLocation();
112
+ // location: { latitude, longitude, accuracy, altitude, bearing, speed, time }
113
+
114
+ // Codificar array de coordenadas a polyline encoded
115
+ let sEncoded = new GpsTools().encode(["38.87, -6.82", "40.42, -3.70"]);
116
+
117
+ // Decodificar polyline encoded
118
+ let locations = new GpsTools().decode("moflFxmrh@kkmHca_R");
119
+ // locations: array de { latitude, longitude }
120
+
121
+ // Simplificar polyline (reducir puntos manteniendo la forma)
122
+ let simplified = new GpsTools().simplifyPolyline({
123
+ polyline : [{ latitude: 43.104, longitude: -3.4261 }, /* ... */],
124
+ tolerance: 3000 // En metros. Mayor = menos vertices
125
+ });
126
+
127
+ // Añadir metadatos EXIF de localización a una imagen
128
+ new GpsTools().addExifLocationToFile({
129
+ file : "foto.jpg",
130
+ latitude : 40.4165000,
131
+ longitude: -3.7025600
132
+ });
133
+
134
+ // Calcular ruta con app externa
135
+ new GpsTools().routeTo({
136
+ sourceLatitude : 40.4167747,
137
+ sourceLongitude : -3.70379019,
138
+ destinationLatitude : 41.3850632,
139
+ destinationLongitude: 2.1734035,
140
+ source : "google_maps" // internal / external / google_maps / osmand / osmand_plus
141
+ });
142
+ ```
143
+
144
+ ### 3.5 Camara y Archivos
145
+
146
+ ```javascript
147
+ // === Tomar foto con la camara (prop tipo VD) ===
148
+ function takePicture() {
149
+ let control = getControl("MAP_CAMERA");
150
+ if (!control) return;
151
+
152
+ control.takePicture({
153
+ filename : "foto_" + Date.now() + ".jpg",
154
+ saveToGallery: true,
155
+ width : 360,
156
+ height : 360,
157
+ onFinished : function(sFileName) {
158
+ if (!sFileName) {
159
+ ui.showToast("Error de camara");
160
+ } else {
161
+ ui.showToast("Foto capturada");
162
+ ui.openFile(sFileName);
163
+ }
164
+ }
165
+ });
166
+ }
167
+
168
+ // === Grabar video ===
169
+ function record() {
170
+ let control = getControl("MAP_CAMERA");
171
+ if (!control) return;
172
+
173
+ control.record({
174
+ quality : 80,
175
+ maxDuration : 10000, // milisegundos
176
+ maxFileSize : 10485760, // bytes (10MB)
177
+ withMicAudio: true,
178
+ onFinished : function(sFileName) {
179
+ if (sFileName) {
180
+ ui.openFile(sFileName);
181
+ }
182
+ }
183
+ });
184
+ }
185
+
186
+ // === Controles de camara ===
187
+ control.stopRecording();
188
+ control.startPreview();
189
+ control.stopPreview();
190
+ control.isCameraOpened();
191
+ control.isAutoFocus();
192
+ control.setAutoFocus(true);
193
+ control.getSupportedAspectRatios();
194
+
195
+ // === Flash modes ===
196
+ control.setFlashMode("on"); // Siempre encendido al tomar foto
197
+ control.setFlashMode("off"); // Siempre apagado
198
+ control.setFlashMode("torch"); // Siempre encendido (linterna)
199
+ control.setFlashMode("auto"); // Automático según sensor de luz
200
+ control.setFlashMode("red_eye"); // Anti ojos rojos
201
+ let mode = control.getFlashMode();
202
+
203
+ // === Cambiar camara frontal/trasera ===
204
+ let sCamera = control.getCamera();
205
+ control.setCamera(sCamera == "front" ? "back" : "front");
206
+
207
+ // === Seleccionar archivo del dispositivo ===
208
+ ui.pickFile({
209
+ targetProperty : "MAP_ADJUNTO",
210
+ fileTypes : "jpg,png,pdf",
211
+ allowMultipleSelection: true,
212
+ resolveFileName : true,
213
+ showSearch : true,
214
+ initialDirectory : appData.getFilesPath(),
215
+ onFinishPicking : function(sAllFiles) {
216
+ for (let sKey in sAllFiles) {
217
+ let file = sAllFiles[sKey];
218
+ console.log("Nombre: " + file.name + " Extension: " + file.extension);
219
+ }
220
+ }
221
+ });
222
+
223
+ // === Abrir un archivo con la app predeterminada ===
224
+ ui.openFile(sPath);
225
+
226
+ // === Abrir URL en navegador externo ===
227
+ ui.openUrl("https://www.ejemplo.com");
228
+
229
+ // === FileManager ===
230
+ let fm = new FileManager();
231
+ if (fm.fileExists("archivo.txt") === 0) {
232
+ let contenido = fm.readFile("archivo.txt");
233
+ }
234
+ fm.saveFile("archivo.txt", "contenido", false); // false = sobreescribir
235
+ fm.delete("temporal.txt");
236
+ let nSize = fm.getSize("archivo.txt");
237
+ ```
238
+
239
+ #### startCamera(params) - Capturar una foto o un vídeo sobre un campo
240
+
241
+ Abre la cámara y guarda el resultado en el campo indicado. A diferencia de `control.takePicture()`, que necesita un control de cámara en la pantalla, aquí no hace falta pintar nada: la captura ocurre en su propia pantalla y al terminar el fichero queda en el campo.
242
+
243
+ ```javascript
244
+ // Foto en movimiento, con la cámara del framework
245
+ ui.startCamera({
246
+ propName : "MAP_FOTO",
247
+ type : "photo",
248
+ useInternalCamera: true,
249
+ motionPhoto : true,
250
+ onSuccess : function(sFileName) {
251
+ ui.showToast("Foto guardada: " + sFileName);
252
+ },
253
+ onCancelled : function() {
254
+ ui.showToast("Captura cancelada");
255
+ }
256
+ });
257
+
258
+ // Vídeo de 30 segundos como máximo
259
+ ui.startCamera({
260
+ propName : "MAP_VIDEO",
261
+ type : "video",
262
+ maxDuration: 30
263
+ });
264
+
265
+ // Forma corta: nombre del campo y tipo
266
+ ui.startCamera("MAP_FOTO", "photo");
267
+ ```
268
+
269
+ | Parámetro | Tipo | Default | Descripción |
270
+ |-----------|------|---------|-------------|
271
+ | `propName` | string | — | Campo donde se guarda el nombre del fichero capturado |
272
+ | `type` | string | `"photo"` | `"photo"`, `"video"` o `"attach"` (este último abre el selector de ficheros en vez de la cámara) |
273
+ | `useInternalCamera` | boolean | `false` | Captura con la cámara que trae el framework en vez de abrir la app de cámara del dispositivo |
274
+ | `motionPhoto` | boolean | `false` | Captura una **foto en movimiento**: un JPG con un clip de vídeo corto embebido detrás. Solo aplica a `type: "photo"` |
275
+ | `size` | number | `0` | Tamaño máximo del fichero en KB. `0` = sin límite |
276
+ | `width` | number | `-1` | Ancho máximo de la foto en píxeles |
277
+ | `height` | number | `-1` | Alto máximo de la foto en píxeles |
278
+ | `quality` | number | `90` | Calidad JPEG de la foto (0-100) |
279
+ | `maxDuration` | number | `-1` | Duración máxima del vídeo en segundos |
280
+ | `onSuccess` | function | — | Recibe el nombre del fichero capturado |
281
+ | `onCancelled` | function | — | Se invoca si el usuario cancela la captura |
282
+
283
+ > **Las fotos en movimiento necesitan `useInternalCamera: true`.** Así funcionan en cualquier versión de Android, porque la captura y el montaje del fichero los hace el propio framework. Sin ese parámetro se delega en la app de cámara del dispositivo, que solo puede atender la petición a partir de **Android 16** y únicamente si la implementa: a día de hoy no lo hace ninguna, ni siquiera la de los Pixel, con lo que se obtiene una foto normal sin más aviso.
284
+
285
+ Al capturar una foto en movimiento se **ignoran** `size`, `width`, `height` y `quality`, porque redimensionar o recomprimir la imagen se llevaría por delante el vídeo embebido. El fichero pesa lo que la foto más el clip, del orden de varios megas.
286
+
287
+ #### scanDocument(params) - Escáner de documentos en papel
288
+
289
+ Abre el escáner de documentos del sistema: guía al usuario para encuadrar el papel, detecta los bordes, recorta y endereza la imagen automáticamente, y permite reencuadrar, aplicar filtros y añadir más páginas antes de aceptar. Es asíncrono: devuelve el control de inmediato y el resultado llega por callback.
290
+
291
+ | Parámetro | Tipo | Default | Descripción |
292
+ |-----------|------|---------|-------------|
293
+ | `mode` | string | `"base"` | `"base"` (recorte, rotación y reencuadre), `"baseWithFilters"` (añade filtros de imagen), `"full"` (añade limpieza automática de la imagen: dedos, manchas). Cualquier otro valor da error |
294
+ | `pageLimit` | number | `1` | Máximo de páginas que se pueden escanear en una sesión |
295
+ | `allowGallery` | boolean | `false` | Permite importar la imagen desde la galería en lugar de capturarla con la cámara |
296
+ | `outputJpg` | boolean | `true` | Genera un JPG por página |
297
+ | `outputPdf` | boolean | `false` | Genera además un PDF con todas las páginas |
298
+ | `onSuccess` | function | — | Recibe un array con los nombres de los ficheros generados |
299
+ | `onError` | function | — | **Obligatorio**. Recibe el mensaje de error |
300
+ | `onCancelled` | function | — | **Obligatorio**. Se invoca si el usuario cancela el escaneo |
301
+
302
+ Hay que dejar activo al menos un formato de salida: si se ponen `outputJpg` y `outputPdf` a `false`, la llamada falla.
303
+
304
+ Los ficheros se escriben en la carpeta de ficheros de la aplicación (`appData.getFilesPath()`) con el prefijo `scan_`, y el array de `onSuccess` trae **solo el nombre del fichero**, no la ruta completa: primero los JPG (uno por página, en orden) y al final el PDF, si se pidió.
305
+
306
+ ```javascript
307
+ ui.scanDocument({
308
+ mode : "baseWithFilters",
309
+ pageLimit : 3,
310
+ allowGallery: true,
311
+ outputJpg : true,
312
+ outputPdf : true,
313
+ onSuccess : function(aFiles) {
314
+ for (let i = 0; i < aFiles.length; i++) {
315
+ console.log("Escaneado: " + aFiles[i]);
316
+ }
317
+ // Guardar la primera página en un campo de imagen
318
+ self.MAP_DOCUMENTO = aFiles[0];
319
+ self.save();
320
+ },
321
+ onError : function(sMessage) {
322
+ ui.showToast("Error al escanear: " + sMessage);
323
+ },
324
+ onCancelled : function() {
325
+ ui.showToast("Escaneo cancelado");
326
+ }
327
+ });
328
+ ```
329
+
330
+ El escáner lo aporta Google Play Services y su módulo se descarga bajo demanda la primera vez que se usa; en dispositivos sin servicios de Google la llamada termina en `onError`.
331
+
332
+ #### recognizeText(params) - OCR de una imagen
333
+
334
+ Reconoce el texto (alfabeto latino) de una imagen del dispositivo. Asíncrono, el resultado llega por callback. Encaja detrás de `scanDocument` para digitalizar un papel y leer su contenido.
335
+
336
+ | Parámetro | Tipo | Default | Descripción |
337
+ |-----------|------|---------|-------------|
338
+ | `path` | string | — | **Obligatorio**. Imagen a reconocer |
339
+ | `onSuccess` | function | — | **Obligatorio**. Recibe el texto (o el objeto con las líneas, si `detail`) |
340
+ | `onError` | function | — | **Obligatorio**. Recibe el mensaje de error |
341
+ | `roi` | objeto | toda la imagen | Región a reconocer: `{left, top, width, height}`. Valores ≤ 1 se interpretan como fracción del tamaño de la imagen; mayores, como píxeles |
342
+ | `scale` | number | `1` | Amplía el recorte antes de reconocer |
343
+ | `grayscale` | boolean | `false` | Desatura la imagen antes de reconocer |
344
+ | `detail` | boolean | `false` | Devuelve un objeto con las líneas y su geometría en lugar de una cadena |
345
+
346
+ Sin `roi`, `scale` ni `grayscale` la imagen se reconoce tal cual, resolviendo su rotación EXIF. En cuanto se pide cualquiera de los tres, la imagen se decodifica, se rota según EXIF y se preprocesa antes de reconocerla.
347
+
348
+ **Recortar es la palanca principal cuando el texto es pequeño** (una matrícula, la banda de caracteres del reverso de un DNI): el reconocedor reescala la imagen internamente, así que cuanto menos sobre en el encuadre, más resolución le queda a cada carácter.
349
+
350
+ Con `detail: true`, `onSuccess` recibe:
351
+
352
+ ```javascript
353
+ {
354
+ text : "…", // el texto completo, igual que sin detail
355
+ lines: [ // ordenadas por posición vertical
356
+ { text: "…", confidence: 0.87, angle: 0.4,
357
+ left: 24, top: 512, width: 640, height: 28 }
358
+ ]
359
+ }
360
+ ```
361
+
362
+ Las líneas vienen **ordenadas por su coordenada vertical**, no en el orden de los bloques reconocidos: `text` sigue el orden de los bloques, que no tiene por qué coincidir con el orden de lectura de la página.
363
+
364
+ ```javascript
365
+ // Reconocer sólo la banda inferior, ampliada al doble y en escala de grises
366
+ ui.recognizeText({
367
+ path : "scan_a1b2c3.jpg",
368
+ roi : { left: 0, top: 0.62, width: 1, height: 0.38 },
369
+ scale : 2,
370
+ grayscale: true,
371
+ detail : true,
372
+ onSuccess: function(result) {
373
+ for (let i = 0; i < result.lines.length; i++) {
374
+ console.log(result.lines[i].confidence + " -> " + result.lines[i].text);
375
+ }
376
+ },
377
+ onError : function(sMessage) { ui.showToast(sMessage); }
378
+ });
379
+ ```
380
+
381
+ No se puede restringir el alfabeto reconocido: el modelo es de texto latino general y aplica su propio criterio, así que sobre secuencias que no son palabras (códigos, matrículas, caracteres de control) hay que validar el resultado por otra vía — un dígito de control, una expresión regular o un formato conocido.
382
+
383
+ ### 3.6 Firma Digital
384
+
385
+ La firma digital se implementa con un campo de tipo `IMG` con `readonly=false`:
386
+
387
+ ```xml
388
+ <!-- En el XML -->
389
+ <prop name="MAP_FIRMA" type="IMG" visible="7" class="propFirma" />
390
+ ```
391
+
392
+ ```css
393
+ /* En default.css (ver topico 02 para detalles de CSS) */
394
+ .propFirma {
395
+ img-sign: bt_Firma.png;
396
+ img-sign-sel: bt_Firma_sel.png;
397
+ sign-title: "Firme aquí";
398
+ sign-clear-text: "Borrar";
399
+ sign-save-text: "Guardar";
400
+ }
401
+ ```
402
+
403
+ ```javascript
404
+ // En el <onchange> de MAP_FIRMA
405
+ function onFirmaCapturada() {
406
+ let firma = self.MAP_FIRMA;
407
+ if (isEmpty(firma)) {
408
+ mostrarToast("Firma cancelada");
409
+ return;
410
+ }
411
+ self.MAP_FECHA_FIRMA = new Date();
412
+ self.save();
413
+ ui.showToast("Firma capturada");
414
+ ui.refresh("MAP_FIRMA");
415
+ }
416
+ ```
417
+
418
+ ### 3.7 QR/Barcode Scanner
419
+
420
+ ```javascript
421
+ // === Escaneo con camara tipo VD ===
422
+ function doSetOnCodeScanned() {
423
+ let control = getControl("MAP_CAMERA");
424
+ if (!control) return;
425
+
426
+ control.setOnCodeScanned(function(evento) {
427
+ // evento.data = valor escaneado
428
+ // evento.type = tipo de código (qr, datamatrix, barcode, etc.)
429
+ let nResult = ui.msgBox(
430
+ "Valor: " + evento.data + "\nTipo: " + evento.type,
431
+ "Código escaneado. Correcto?", 4
432
+ );
433
+ if (nResult == 6) {
434
+ return true; // Aceptar y dejar de escanear
435
+ } else {
436
+ return false; // Rechazar y seguir escaneando
437
+ }
438
+ });
439
+ }
440
+ ```
441
+
442
+ ### 3.8 Sleep y Timers
443
+
444
+ ```javascript
445
+ // === Sleep - BLOQUEA la UI (usar con extremo cuidado) ===
446
+ ui.sleep(3); // Pausa de 3 segundos
447
+
448
+ // === Ejecutar acción con retardo (PREFERIDO sobre sleep) ===
449
+ // USO CORRECTO: disparar UNA acción puntual tras un retardo corto.
450
+ ui.executeActionAfterDelay("miFuncion", 5); // Ejecuta miFuncion() tras 5 segundos
451
+
452
+ // === ATENCION: NO encadenar executeActionAfterDelay como setInterval ===
453
+ // Para temporizadores continuos (relojes, contadores, polling regular)
454
+ // usar startChronometer. Encadenar executeActionAfterDelay cada segundo
455
+ // consume mucha memoria y ralentiza el dispositivo. Ver seccion 3.10.
456
+ ```
457
+
458
+ ### 3.9 Otros
459
+
460
+ ```javascript
461
+ // Verificar si la app esta en background
462
+ let bBackground = ui.isInBackground();
463
+
464
+ // Traer la app al frente
465
+ ui.returnToForeground();
466
+
467
+ // Enviar email
468
+ ui.sendMail("destino@email.com", "copia@email.com", "Asunto", "Cuerpo", "adjunto.pdf");
469
+
470
+ // Hacer llamada telefonica
471
+ ui.makePhoneCall("+34123456789");
472
+
473
+ // Iniciar grabacion de audio (forma con objeto de parametros; ver seccion 3.10)
474
+ ui.startAudioRecord({
475
+ onComplete: function(sPath) { self.MAP_AUDIO = sPath; ui.refresh("MAP_AUDIO"); },
476
+ onError: function(sError) { ui.showToast(sError); },
477
+ timeout: 0, outputFormat: "mp4", audioEncoder: "he_aac"
478
+ });
479
+ ui.stopAudioRecord();
480
+
481
+ // Date Picker
482
+ ui.showDatePicker({
483
+ initialYear: 2024, initialMonth: 6, initialDay: 15,
484
+ title: "Seleccione fecha",
485
+ onDateSet: function(nYear, nMonth, nDay) {
486
+ self.MAP_FECHA = nDay + "/" + nMonth + "/" + nYear;
487
+ ui.refresh("MAP_FECHA");
488
+ }
489
+ });
490
+
491
+ // Time Picker
492
+ ui.showTimePicker({
493
+ initialHour: 17, initialMinute: 30, is24HoursMode: true,
494
+ onTimeSet: function(nHours, nMinutes) {
495
+ let h = ("0" + nHours).slice(-2);
496
+ let m = ("0" + nMinutes).slice(-2);
497
+ self.MAP_HORA = h + ":" + m;
498
+ ui.refresh("MAP_HORA");
499
+ }
500
+ });
501
+
502
+ // Drag and Drop
503
+ let control = window["MAP_CONTROL"];
504
+ ui.startDrag(control, object);
505
+ ```
506
+