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.
- package/README.md +167 -0
- package/dist/agent/authEnDisco.js +56 -0
- package/dist/agent/authEnDisco.js.map +1 -0
- package/dist/agent/configEnDisco.js +109 -0
- package/dist/agent/configEnDisco.js.map +1 -0
- package/dist/agent/crearProyecto.js +34 -0
- package/dist/agent/crearProyecto.js.map +1 -0
- package/dist/agent/entorno.js +124 -0
- package/dist/agent/entorno.js.map +1 -0
- package/dist/agent/guionizado.js +53 -0
- package/dist/agent/guionizado.js.map +1 -0
- package/dist/agent/instantanea.js +129 -0
- package/dist/agent/instantanea.js.map +1 -0
- package/dist/agent/interrupts.js +83 -0
- package/dist/agent/interrupts.js.map +1 -0
- package/dist/agent/mensajes.js +24 -0
- package/dist/agent/mensajes.js.map +1 -0
- package/dist/agent/modelos.js +42 -0
- package/dist/agent/modelos.js.map +1 -0
- package/dist/agent/normalizar.js +20 -0
- package/dist/agent/normalizar.js.map +1 -0
- package/dist/agent/perfiles.js +103 -0
- package/dist/agent/perfiles.js.map +1 -0
- package/dist/agent/proyecto.js +107 -0
- package/dist/agent/proyecto.js.map +1 -0
- package/dist/agent/puente.js +142 -0
- package/dist/agent/puente.js.map +1 -0
- package/dist/agent/resumenDeTool.js +48 -0
- package/dist/agent/resumenDeTool.js.map +1 -0
- package/dist/agent/skills.js +65 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/textoDeTool.js +113 -0
- package/dist/agent/textoDeTool.js.map +1 -0
- package/dist/agent/turnoReal.js +173 -0
- package/dist/agent/turnoReal.js.map +1 -0
- package/dist/agent/verificador.js +65 -0
- package/dist/agent/verificador.js.map +1 -0
- package/dist/agent/xoneAgent.js +106 -0
- package/dist/agent/xoneAgent.js.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/aprobar.js +82 -0
- package/dist/cli/aprobar.js.map +1 -0
- package/dist/cli/config.js +133 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/consola.js +346 -0
- package/dist/cli/consola.js.map +1 -0
- package/dist/cli/describe.js +53 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/doctor.js +45 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/main.js +520 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/markdown.js +90 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/run.js +164 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spinner.js +89 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/stdio.js +193 -0
- package/dist/cli/stdio.js.map +1 -0
- package/dist/cli/tema.js +40 -0
- package/dist/cli/tema.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/core/bitacora.js +30 -0
- package/dist/core/bitacora.js.map +1 -0
- package/dist/core/config.js +236 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contextos.js +56 -0
- package/dist/core/contextos.js.map +1 -0
- package/dist/core/deps.js +51 -0
- package/dist/core/deps.js.map +1 -0
- package/dist/core/diff.js +79 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/esqueleto.js +366 -0
- package/dist/core/esqueleto.js.map +1 -0
- package/dist/core/events.js +2 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/modelos.js +85 -0
- package/dist/core/modelos.js.map +1 -0
- package/dist/core/notify.js +106 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/ports.js +114 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/turno.js +127 -0
- package/dist/core/turno.js.map +1 -0
- package/dist/vendor/hitl.js +103 -0
- package/dist/vendor/hitl.js.map +1 -0
- package/dist/vendor/skillLoaders/catalog.js +74 -0
- package/dist/vendor/skillLoaders/catalog.js.map +1 -0
- package/dist/vendor/tokenTracking.js +58 -0
- package/dist/vendor/tokenTracking.js.map +1 -0
- package/package.json +41 -0
- package/skills/xone-debugging/SKILL.md +52 -0
- package/skills/xone-debugging/references/faq.md +561 -0
- package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
- package/skills/xone-development/SKILL.md +105 -0
- package/skills/xone-development/references/anti-patrones.md +94 -0
- package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
- package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
- package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
- package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
- package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
- package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
- package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
- package/skills/xone-development/references/datos/appdata.md +611 -0
- package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
- package/skills/xone-development/references/datos/http.md +366 -0
- package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
- package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
- package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
- package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
- package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
- package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
- package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
- package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
- package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
- package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
- package/skills/xone-development/references/indice-completo.md +77 -0
- package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
- package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
- package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
- package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
- package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
- package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
- package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
- package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
- package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
- package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
- package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
- package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
- package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
- package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
- package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
- package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
- package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
- package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
- package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
- package/skills/xone-development/references/tipos-de-prop.md +28 -0
- package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
- package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
- package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
- package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
- package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
- package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
- package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
- package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
- package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
- package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
- package/skills/xone-development/references/xml-ui/mapas.md +685 -0
- package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
- package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
- package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
- package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
- package/skills/xone-plan-builder/SKILL.md +183 -0
- package/skills/xone-plan-builder/agents/openai.yaml +5 -0
- package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
- package/skills/xone-project-generator/SKILL.md +111 -0
- package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
- package/skills/xone-project-generator/references/colecciones-base.md +16 -0
- package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
- package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
- package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
- package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
- package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
- package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
- package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
- package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
- package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
- package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
- package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
- package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
- package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
- package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
- package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
- package/skills/xone-review/SKILL.md +189 -0
- package/skills/xone-spec-builder/SKILL.md +235 -0
- package/skills/xone-spec-builder/agents/openai.yaml +5 -0
- package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
- package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
- 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
|
+
|