diff --git a/electron/src/handlers/connection.handler.ts b/electron/src/handlers/connection.handler.ts index 16741eae..be29e4c4 100644 --- a/electron/src/handlers/connection.handler.ts +++ b/electron/src/handlers/connection.handler.ts @@ -67,6 +67,7 @@ export class ConnectionHandler implements Handler { ws.send(JSON.stringify(new responseModelUpdateSettings().fromObject({ outputProfiles: this.settingsHandler.outputProfiles, events: this.getEnabledEvents(), + savedGeoLocations: this.settingsHandler.savedGeoLocations }))); } }); @@ -151,7 +152,8 @@ export class ConnectionHandler implements Handler { * @deprecated */ quantityEnabled: false, - serverUUID: this.settingsHandler.getServerUUID() + serverUUID: this.settingsHandler.getServerUUID(), + savedGeoLocations: this.settingsHandler.savedGeoLocations as { name: string, latitude: number, longitude: number }[] }); if (request && request.deviceId) { diff --git a/electron/src/handlers/scans.handler.ts b/electron/src/handlers/scans.handler.ts index 13dde0c7..df7d437b 100644 --- a/electron/src/handlers/scans.handler.ts +++ b/electron/src/handlers/scans.handler.ts @@ -261,6 +261,7 @@ export class ScansHandler implements Handler { google_sheets: null, javascript_function: null, static_text: null, + geolocation: null, }; // Search if there is a corresponding Output component to assign to the NULL variables let keys = Object.keys(variables); diff --git a/electron/src/handlers/settings.handler.ts b/electron/src/handlers/settings.handler.ts index 305d9f6e..07671704 100644 --- a/electron/src/handlers/settings.handler.ts +++ b/electron/src/handlers/settings.handler.ts @@ -81,7 +81,9 @@ export class SettingsHandler implements Handler { get onSmartphoneChargeCommand() { return this.settings.onSmartphoneChargeCommand } - + get savedGeoLocations() { + return this.store.get(Config.STORAGE_SAVED_GEOLOCATIONS, []); + } async onWsMessage(ws: WebSocket, message: any, req: http.IncomingMessage): Promise { throw new Error("Method not implemented."); return message; diff --git a/ionic/src/app/app.module.ts b/ionic/src/app/app.module.ts index 62048be4..8544e133 100644 --- a/ionic/src/app/app.module.ts +++ b/ionic/src/app/app.module.ts @@ -56,6 +56,7 @@ import { MyApp } from './app.component'; import { ComponentEditorImagePage } from '../pages/component-editor/component-editor-image/component-editor-image'; import { ImageViewerPage } from '../pages/image-viewer/image-viewer'; import { ExportOutputTemplatePopoverPage } from '../pages/export-output-template-popover/export-output-template-popover'; +import { ComponentEditorGeolocationPage } from '../pages/component-editor/component-editor-geolocation/component-editor-geolocation'; @NgModule({ declarations: [ @@ -67,6 +68,7 @@ import { ExportOutputTemplatePopoverPage } from '../pages/export-output-template ComponentEditorAlertPage, ComponentEditorBarcodePage, ComponentEditorBeepPage, + ComponentEditorGeolocationPage, ComponentEditorCsvLookupPage, ComponentEditorCsvUpdatePage, ComponentEditorGSheetUpdatePage, @@ -138,6 +140,7 @@ import { ExportOutputTemplatePopoverPage } from '../pages/export-output-template ComponentEditorAlertPage, ComponentEditorBarcodePage, ComponentEditorBeepPage, + ComponentEditorGeolocationPage, ComponentEditorCsvLookupPage, ComponentEditorCsvUpdatePage, ComponentEditorGSheetUpdatePage, diff --git a/ionic/src/assets/i18n/ar.json b/ionic/src/assets/i18n/ar.json index d2e35fb0..bac2f46d 100644 --- a/ionic/src/assets/i18n/ar.json +++ b/ionic/src/assets/i18n/ar.json @@ -412,6 +412,15 @@ "medium": "متوسط", "fast": "سريع", "testAudio": "اختبار الصوت", + "savedLocation": "الموقع المحفوظ", + "geoLocationSavedLocations": "المواقع المحفوظة", + "geoLocationDescription": "يحصل على إحداثيات GPS (خط العرض وخط الطول). استخدم "المواقع المحفوظة" للحصول على اسم مخصص بدلاً من الإحداثيات الرقمية في النتيجة. للحصول على معلومات دقيقة عن الموقع، تتعلق باللحظة الدقيقة لاكتساب الباركود، تأكد من وضع مكون الـ GEOLOCATION بعد مكونات الحجب مثل BARCODE.", + "geoLocationAssignNames": "قم بتسمية الإحداثيات بحيث عند مسح الباركود بالقرب من هذه المواقع يتم عرض الاسم بدلاً من الإحداثيات.", + "geoLocationCoordinatesTutorial": "كيف يمكن إيجاد الإحداثيات؟", + "geoLocationNotfound": "لم يتم العثور على مواقع محفوظة", + "geoLocationAddLocation": "أضف موقعاً", + "geoLocationMaxDistanceFromSavedLocation": "المسافة القصوى من الموقع المحفوظ", + "geoLocationMaxDistanceFromSavedLocationDescription": "المسافة القصوى بالكيلومترات من الموقع المحفوظ لعرض اسمه بدلاً من الإحداثيات", "title": "العنوان", "message": "الرسالة", "optionsList": "قائمة الخيارات", diff --git a/ionic/src/assets/i18n/en.json b/ionic/src/assets/i18n/en.json index 1a6f6a59..68d9b1d1 100644 --- a/ionic/src/assets/i18n/en.json +++ b/ionic/src/assets/i18n/en.json @@ -412,6 +412,15 @@ "medium": "Medium", "fast": "Fast", "testAudio": "Test audio", + "savedLocation": "Saved location ", + "geoLocationSavedLocations": "Saved locations", + "geoLocationDescription": "Collect GPS coordinates (latitude and longitude). Use "Saved locations" to output a custom location name instead. To get accurate location information, that is reffering at moment of the the barcode acquisition, make sure to put the GEOLOCATION component after blocking components such as BARCODE.", + "geoLocationAssignNames": "Assign names to coordinates so that when scanning barcodes near that location its name will be outputed instead of coordinates.", + "geoLocationCoordinatesTutorial": "How to find coordinates?", + "geoLocationNotfound":" No Saved locations found'", + "geoLocationAddLocation": "Add location", + "geoLocationMaxDistanceFromSavedLocation": "Max distance from saved location", + "geoLocationMaxDistanceFromSavedLocationDescription": "The maximum distance in kilometers from the saved location to output its name instead of coordinates", "title": "Title", "message": "Message", "optionsList": "Options list", diff --git a/ionic/src/assets/i18n/es.json b/ionic/src/assets/i18n/es.json index 14b80662..e00f320c 100644 --- a/ionic/src/assets/i18n/es.json +++ b/ionic/src/assets/i18n/es.json @@ -412,6 +412,15 @@ "medium": "Medio", "fast": "Rápido", "testAudio": "Prueba de audio", + "savedLocation": "Ubicación guardada", + "geoLocationSavedLocations": "Ubicaciones guardadas", + "geoLocationDescription": "Obtiene coordenadas GPS (latitud y longitud). Utiliza "Ubicaciones guardadas" para obtener un nombre personalizado en la salida en lugar de las coordenadas numéricas. Para obtener información precisa de la ubicación que se refiera al instante exacto de la captura del código de barras, asegúrate de colocar el componente GEOLOCATION después de componentes que bloquean como BARCODE.", + "geoLocationAssignNames": "Asigna nombres a las coordenadas para que, al escanear códigos de barras cerca de estas ubicaciones, se muestre el nombre en lugar de las coordenadas.", + "geoLocationCoordinatesTutorial": "¿Cómo encontrar las coordenadas?", + "geoLocationNotfound": "No se encontraron ubicaciones guardadas", + "geoLocationAddLocation": "Agregar ubicación", + "geoLocationMaxDistanceFromSavedLocation": "Distancia máxima desde la ubicación guardada", + "geoLocationMaxDistanceFromSavedLocationDescription": "La distancia máxima en kilómetros desde la ubicación guardada para mostrar su nombre en lugar de las coordenadas", "title": "Título", "message": "Mensaje", "optionsList": "Lista de opciones", diff --git a/ionic/src/assets/i18n/it.json b/ionic/src/assets/i18n/it.json index c4e8b181..96fd76b6 100644 --- a/ionic/src/assets/i18n/it.json +++ b/ionic/src/assets/i18n/it.json @@ -412,6 +412,15 @@ "medium": "Medio", "fast": "Veloce", "testAudio": "Test audio", + "savedLocation": "Posizione salvata", + "geoLocationSavedLocations": "Posizioni salvate", + "geoLocationDescription": "Ottiene le coordinate GPS (latitudine e longitudine). Usa "Posizioni salvate" per avere in uscita un nome personalizzato invece delle coordinate numeriche. Per ottenere informazioni precise sulla posizione, che facciano riferimento all'esatto istante di acquisizione del codice a barre, assicurati di inserire il componente GEOLOCATION dopo componenti bloccanti come ad esempio BARCODE.", + "geoLocationAssignNames": "Assegna nomi alle coordinate in modo che, quando si scansionano codici a barre vicino a queste posizioni, venga visualizzato il nome invece delle coordinate.", + "geoLocationCoordinatesTutorial": "Come trovare le coordinate?", + "geoLocationNotfound": "Nessuna posizione salvata", + "geoLocationAddLocation": "Aggiungi posizione", + "geoLocationMaxDistanceFromSavedLocation": "Distanza massima dalla posizione salvata", + "geoLocationMaxDistanceFromSavedLocationDescription": "The maximum distance in kilometers from the saved location to output its name instead of coordinates", "title": "Titolo", "message": "Messaggio", "optionsList": "Elenco delle opzioni", diff --git a/ionic/src/components/output-component-component/output-component-component.ts b/ionic/src/components/output-component-component/output-component-component.ts index 0e2c847e..148220aa 100644 --- a/ionic/src/components/output-component-component/output-component-component.ts +++ b/ionic/src/components/output-component-component/output-component-component.ts @@ -21,6 +21,7 @@ import { ComponentEditorVariablePage } from '../../pages/component-editor/compon import { ComponentEditorWooCommercePage } from '../../pages/component-editor/component-editor-woocommerce/component-editor-woocommerce'; import { UtilsProvider } from '../../providers/utils/utils'; import { ComponentEditorImagePage } from '../../pages/component-editor/component-editor-image/component-editor-image'; +import { ComponentEditorGeolocationPage } from '../../pages/component-editor/component-editor-geolocation/component-editor-geolocation'; @Component({ @@ -64,6 +65,7 @@ export class OutputComponentComponent { case 'variable': editor = ComponentEditorVariablePage; break; case 'woocommerce': editor = ComponentEditorWooCommercePage; break; case 'image': editor = ComponentEditorImagePage; break; + case 'geolocation': editor = ComponentEditorGeolocationPage; break; } let modal = this.modalCtrl .create(editor, { outputBlock: this.outputBlock }, { enableBackdropDismiss: false, showBackdrop: true }); diff --git a/ionic/src/config.ts b/ionic/src/config.ts index 75d993ec..15fc79c3 100644 --- a/ionic/src/config.ts +++ b/ionic/src/config.ts @@ -59,6 +59,7 @@ export class Config { public static STORAGE_SETTINGS = 'storage_settings'; public static STORAGE_LAST_VERSION = 'storage_last_version'; public static STORAGE_LICENSE_EVER_ACTIVATED = 'storage_license_ever_activated'; + public static STORAGE_SAVED_GEOLOCATIONS = 'storage_saved_geolocations'; public static GAPIS_CREDENTIALS = { // 1. Generate credentials: https://console.cloud.google.com/apis/ diff --git a/ionic/src/models/output-block.model.ts b/ionic/src/models/output-block.model.ts index 1d0aa8b5..c99f9e42 100644 --- a/ionic/src/models/output-block.model.ts +++ b/ionic/src/models/output-block.model.ts @@ -51,7 +51,7 @@ export class OutputBlockModel { * * Warning: remeber to update also edit-output-block-pop-over.ts/onHelpClick() method when chaning this field. */ - type: 'key' | 'text' | 'variable' | 'function' | 'barcode' | 'delay' | 'if' | 'endif' | 'http' | 'run' | 'select_option' | 'image' | 'beep' | 'csv_lookup' | 'csv_update' | 'alert' | 'date_time' | 'woocommerce' | 'google_sheets'; + type: 'key' | 'text' | 'variable' | 'function' | 'barcode' | 'delay' | 'if' | 'endif' | 'http' | 'run' | 'select_option' | 'image' | 'beep' | 'csv_lookup' | 'csv_update' | 'alert' | 'date_time' | 'woocommerce' | 'google_sheets' | 'geolocation'; /** * When true means that the user doesn't want to type or append to files * the component value but instead he wants to acquire the data, and use it @@ -207,6 +207,13 @@ export class OutputBlockModel { imageHd?: boolean; outputImagePath?: string; + + /** + * Parameter for the GEOLOCATION component + */ + outputMode?: 'coordinates' | 'savedLocation'; + maxDistanceFromSavedLocation?: number; // meters + static FindEndIfIndex(outputBlocks: OutputBlockModel[], startFrom = 0): number { let skip = 0; for (let i = startFrom; i < outputBlocks.length; i++) { diff --git a/ionic/src/models/response.model.ts b/ionic/src/models/response.model.ts index f5fcce62..412e151a 100644 --- a/ionic/src/models/response.model.ts +++ b/ionic/src/models/response.model.ts @@ -41,13 +41,14 @@ export class responseModelHelo extends responseModel { outputProfiles: OutputProfileModel[]; events: string[]; serverUUID: string; + savedGeoLocations: { name: string, latitude: number, longitude: number }[]; /** * @deprecated Use OutputProfiles instead */ quantityEnabled: boolean; - public fromObject(obj: ({ version: string, outputProfiles: OutputProfileModel[], quantityEnabled: boolean, events: string[], serverUUID:string })) { + public fromObject(obj: ({ version: string, outputProfiles: OutputProfileModel[], quantityEnabled: boolean, events: string[], serverUUID: string, savedGeoLocations: { name: string, latitude: number, longitude: number }[] })) { this.outputProfiles = obj.outputProfiles; if (obj.events) { this.events = obj.events; @@ -58,6 +59,7 @@ export class responseModelHelo extends responseModel { this.outputProfiles = obj.outputProfiles; this.quantityEnabled = obj.quantityEnabled; this.serverUUID = obj.serverUUID; + this.savedGeoLocations = obj.savedGeoLocations || []; return this; } } @@ -117,14 +119,16 @@ export class responseModelUpdateSettings extends responseModel { // Warning: these same settings are also communicated in the HELO response outputProfiles: OutputProfileModel[]; events: string[]; + savedGeoLocations: { name: string, latitude: number, longitude: number }[]; - public fromObject(obj: ({ outputProfiles: OutputProfileModel[], events: string[] })) { + public fromObject(obj: ({ outputProfiles: OutputProfileModel[], events: string[], savedGeoLocations: { name: string, latitude: number, longitude: number }[] })) { this.outputProfiles = obj.outputProfiles; if (obj.events) { this.events = obj.events; } else { this.events = []; } + this.savedGeoLocations = obj.savedGeoLocations || []; return this; } } diff --git a/ionic/src/models/scan.model.ts b/ionic/src/models/scan.model.ts index f133fd16..ed70443c 100755 --- a/ionic/src/models/scan.model.ts +++ b/ionic/src/models/scan.model.ts @@ -78,6 +78,7 @@ export class ScanModel { case 'alert': return '' case 'woocommerce': return block.value; case 'google_sheets': return block.value; + case 'geolocation': return block.value; default: return ''; } }).join(fieldSeparator).replace(/\s+/g, ' '); diff --git a/ionic/src/models/settings.model.ts b/ionic/src/models/settings.model.ts index 7c430f35..f239e564 100644 --- a/ionic/src/models/settings.model.ts +++ b/ionic/src/models/settings.model.ts @@ -45,4 +45,5 @@ export class SettingsModel { autoUpdate: boolean = true; onSmartphoneChargeCommand: string = ''; maxScanSessionsNumber: number = 2000; // Update also SettingsPage.MAX_SCAN_SESSION_NUMBER_UNLIMITED + savedGeoLocations:{ name: string, latitude: number, longitude: number }[] = []; } diff --git a/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.html b/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.html new file mode 100644 index 00000000..d2c4f13c --- /dev/null +++ b/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.html @@ -0,0 +1,56 @@ + + +

+
+ + + {{ 'geoLocationSavedLocations' | translate }} + + +

{{ 'geoLocationAssignNames' | translate }}

+

+ + {{ 'geoLocationCoordinatesTutorial' | translate }} + +

+
+ + + + + + + + + + + + + + + + + + + +

+ {{ 'geoLocationNotfound' | translate }} +

+ + + + + + + + +
+

{{ 'geoLocationMaxDistanceFromSavedLocationDescription' | translate }}

+
+ +
diff --git a/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.scss b/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.scss new file mode 100644 index 00000000..82e40c97 --- /dev/null +++ b/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.scss @@ -0,0 +1,13 @@ +page-component-editor-geolocation { + ion-col { + padding: 0 10px 0 0 !important; + + input { + margin-top: 1px !important + } + } + + .fields ion-label { + margin: 0; + } +} diff --git a/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.ts b/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.ts new file mode 100644 index 00000000..4b8570d8 --- /dev/null +++ b/ionic/src/pages/component-editor/component-editor-geolocation/component-editor-geolocation.ts @@ -0,0 +1,58 @@ +import { Component } from '@angular/core'; +import { NavParams } from 'ionic-angular'; +import { OutputBlockModel } from '../../../models/output-block.model'; +import { ElectronProvider } from '../../../providers/electron/electron'; +import { UtilsProvider } from '../../../providers/utils/utils'; +import { Config } from '../../../config'; + +@Component({ + selector: 'page-component-editor-geolocation', + templateUrl: 'component-editor-geolocation.html', +}) +export class ComponentEditorGeolocationPage { + + public outputBlock: OutputBlockModel; + public savedGeoLocations: { name: string, latitude: number, longitude: number }[] = []; + + constructor( + public navParams: NavParams, + private utilsProvider: UtilsProvider, + private electronProvider: ElectronProvider, + ) { + this.outputBlock = this.navParams.get('outputBlock'); + + this.savedGeoLocations = this.electronProvider.store.get(Config.STORAGE_SAVED_GEOLOCATIONS, []); + } + + ionViewWillUnload() { + this.outputBlock.value = Math.random() + ''; // trigger Save & Apply + // keep only locations that have actual values and that can be converted to numbers + this.savedGeoLocations = this.savedGeoLocations.filter(x => x.latitude && x.longitude && !isNaN(x.latitude) && !isNaN(x.longitude)); + // convert to numbers + this.savedGeoLocations = this.savedGeoLocations.map(x => { return { name: x.name, latitude: parseFloat(x.latitude.toString()), longitude: parseFloat(x.longitude.toString()) } }); + // save + this.electronProvider.store.set(Config.STORAGE_SAVED_GEOLOCATIONS, this.savedGeoLocations); + } + + ionViewDidLoad() { + console.log('ionViewDidLoad ComponentEditorGeolocationPage'); + } + + async addSavedLocation() { + this.savedGeoLocations = [...this.savedGeoLocations, { name: await this.utilsProvider.text('savedLocation') + (this.savedGeoLocations.length + 1).toString(), latitude: null, longitude: null }]; + } + + deleteSavedLocation(removeIndex: number) { + this.savedGeoLocations = this.savedGeoLocations.filter((x, index) => index != removeIndex); + } + + onLatitudeChange(index: number) { + let latitude = (this.savedGeoLocations[index].latitude || 0).toString(); + if (latitude.indexOf(',') > -1) { + let latitudeLongitude = latitude.split(','); + this.savedGeoLocations[index].latitude = parseFloat(latitudeLongitude[0] || '0'); + this.savedGeoLocations[index].longitude = parseFloat(latitudeLongitude[1] || '0'); + } + } + +} diff --git a/ionic/src/pages/component-editor/component-editor-image/component-editor-image.html b/ionic/src/pages/component-editor/component-editor-image/component-editor-image.html index 5eb11931..3910e7b1 100644 --- a/ionic/src/pages/component-editor/component-editor-image/component-editor-image.html +++ b/ionic/src/pages/component-editor/component-editor-image/component-editor-image.html @@ -7,6 +7,8 @@ + + diff --git a/ionic/src/pages/settings/settings.ts b/ionic/src/pages/settings/settings.ts index a8333cf5..29e9d4d0 100644 --- a/ionic/src/pages/settings/settings.ts +++ b/ionic/src/pages/settings/settings.ts @@ -88,6 +88,7 @@ export class SettingsPage implements OnInit, OnDestroy { { name: 'BEEP', value: 'beep', type: 'beep', beepsNumber: 1, beepSpeed: 'medium' }, { name: 'ALERT', value: '', type: 'alert', alertTitle: 'Alert', alertDiscardScanButton: 'Discard scan', alertScanAgainButton: 'Scan again', alertOkButton: 'Ok' }, { name: 'WOOCOMMERCE', value: 'createProduct', type: 'woocommerce', allowOOBExecution: true, fields: [], consumer_key: '', consumer_secret: '', url_woocommerce: '', skipOutput: true, label: null }, + { name: 'GEOLOCATION', value: '', type: 'geolocation', outputMode: 'coordinates', maxDistanceFromSavedLocation: 1, skipOutput: true, label: null }, ]; } diff --git a/ionic/src/theme/variables.scss b/ionic/src/theme/variables.scss index 0c0d2e60..ced3befc 100644 --- a/ionic/src/theme/variables.scss +++ b/ionic/src/theme/variables.scss @@ -59,6 +59,7 @@ $colors: ( output-block-component-date_time: #293b96, output-block-component-woocommerce: #7f54b3, output-block-component-image: #157ae5, + output-block-component-geolocation: #12579c, text: #ffffff, selected: #ebebeb, );