├── .gitignore ├── monkey.jungle ├── .settings └── IQ_IDE.prefs ├── resources ├── drawables │ ├── drawables.xml │ └── launcher_icon.png ├── fonts │ ├── qrcode-1.png │ ├── qrcode-10.png │ ├── qrcode-11.png │ ├── qrcode-12.png │ ├── qrcode-13.png │ ├── qrcode-14.png │ ├── qrcode-15.png │ ├── qrcode-16.png │ ├── qrcode-2.png │ ├── qrcode-3.png │ ├── qrcode-4.png │ ├── qrcode-5.png │ ├── qrcode-6.png │ ├── qrcode-7.png │ ├── qrcode-8.png │ ├── qrcode-9.png │ ├── fonts.xml │ ├── qrcode-9.fnt │ ├── qrcode-1.fnt │ ├── qrcode-2.fnt │ ├── qrcode-3.fnt │ ├── qrcode-4.fnt │ ├── qrcode-5.fnt │ ├── qrcode-6.fnt │ ├── qrcode-8.fnt │ ├── qrcode-15.fnt │ ├── qrcode-16.fnt │ ├── qrcode-10.fnt │ ├── qrcode-11.fnt │ ├── qrcode-12.fnt │ ├── qrcode-13.fnt │ ├── qrcode-14.fnt │ └── qrcode-7.fnt ├── strings.xml ├── properties.xml └── settings.xml ├── README.md ├── source ├── Utils.mc ├── menu │ ├── QrCodeViewerMenuDelegate.mc │ └── DMenu.mc ├── Settings.mc ├── QRCodeViewerDelegate.mc ├── Code.mc ├── QRCodeViewerApp.mc └── QRCodeViewerView.mc ├── .project ├── resources-eng └── strings.xml ├── resources-tha └── strings.xml ├── resources-ces └── strings.xml ├── resources-fre └── strings.xml ├── resources-deu └── strings.xml └── manifest.xml /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | -------------------------------------------------------------------------------- /monkey.jungle: -------------------------------------------------------------------------------- 1 | project.manifest = manifest.xml 2 | 3 | -------------------------------------------------------------------------------- /.settings/IQ_IDE.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | project_manifest=manifest.xml 3 | -------------------------------------------------------------------------------- /resources/drawables/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/fonts/qrcode-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-1.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-10.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-11.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-12.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-13.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-14.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-15.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-16.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-2.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-3.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-4.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-5.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-6.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-7.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-8.png -------------------------------------------------------------------------------- /resources/fonts/qrcode-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/fonts/qrcode-9.png -------------------------------------------------------------------------------- /resources/drawables/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macherel/Connect-IQ-QR-Code-Viewer/HEAD/resources/drawables/launcher_icon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QR Code Viewer 2 | [QR Code Viewer on the Connect IQ App Store](https://apps.garmin.com/en-US/apps/3a83fd85-0c2c-49a3-b46e-69869d6ea3bc) 3 | 4 | A widget that can display QR Code on Garmin watch 5 | -------------------------------------------------------------------------------- /source/Utils.mc: -------------------------------------------------------------------------------- 1 | function join(array, char) { 2 | var result = array[0]; 3 | for(var i=1; i 2 | 3 | QR Code Viewer 4 | 5 | 6 | 7 | 8 | 9 | connectiq.builder 10 | 11 | 12 | 13 | 14 | 15 | connectiq.projectNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/menu/QrCodeViewerMenuDelegate.mc: -------------------------------------------------------------------------------- 1 | using Toybox.WatchUi as Ui; 2 | using Toybox.Application as App; 3 | 4 | class QrCodeViewerMenuDelegate extends Ui.MenuInputDelegate { 5 | 6 | function initialize () { 7 | MenuInputDelegate.initialize (); 8 | } 9 | 10 | function onMenuItem (item) { 11 | System.println("Select code #" + item.userData); 12 | Settings.setCurrentId(item.userData); 13 | Ui.popView(Ui.SLIDE_IMMEDIATE); 14 | } 15 | } -------------------------------------------------------------------------------- /source/Settings.mc: -------------------------------------------------------------------------------- 1 | using Toybox.Application as App; 2 | using Toybox.WatchUi as Ui; 3 | 4 | module Settings { 5 | 6 | var barcodeHeight; 7 | var cacheEnabled; 8 | var currentId; 9 | var displayLabel; 10 | var offsetY; 11 | var retainMenuIndex; 12 | var size; 13 | 14 | function load() { 15 | var app = App.getApp(); 16 | 17 | // Force default value for old version 18 | if(app.getProperty("liVersion")==null) { 19 | app.setProperty("liVersion", 0); 20 | app.setProperty("cacheEnabled", true); 21 | } 22 | 23 | barcodeHeight = app.getProperty("barcodeHeight"); 24 | cacheEnabled = app.getProperty("cacheEnabled"); 25 | currentId = app.getProperty("currentId"); 26 | displayLabel = app.getProperty("displayLabel"); 27 | offsetY = app.getProperty("offsetY"); 28 | retainMenuIndex = app.getProperty("retainMenuIndex"); 29 | size = app.getProperty("size"); 30 | } 31 | 32 | function setCurrentId(id) { 33 | currentId = id; 34 | App.getApp().setProperty("currentId", currentId); 35 | } 36 | } -------------------------------------------------------------------------------- /resources/fonts/fonts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /source/QRCodeViewerDelegate.mc: -------------------------------------------------------------------------------- 1 | using Toybox.WatchUi as Ui; 2 | using Toybox.Application as App; 3 | 4 | class QRCodeViewerDelegate extends Ui.BehaviorDelegate { 5 | 6 | function initialize() { 7 | BehaviorDelegate.initialize(); 8 | } 9 | 10 | function onSelect() { 11 | var app = App.getApp(); 12 | 13 | if(app.enabledCodes.size() > 0) { 14 | var qrCodesMenu = []; 15 | var menuIndex = 0; 16 | for(var i=0; i 2 | QR Code Viewer 3 | 4 | No QR code 5 | Select QR code 6 | 7 | Select QR Code 8 | 9 | Retain menu index 10 | Size 11 | Barcode height 12 | Offset Y 13 | Enable offline mode 14 | Error 15 | Please contact developer 16 | 17 | Display label 18 | Enable 19 | Label 1 20 | Value 1 21 | Type 1 22 | Enable 23 | Label 2 24 | Value 2 25 | Type 2 26 | Enable 27 | Label 3 28 | Value 3 29 | Type 3 30 | Enable 31 | Label 4 32 | Value 4 33 | Type 4 34 | Enable 35 | Label 5 36 | Value 5 37 | Type 5 38 | Enable 39 | Label 6 40 | Value 6 41 | Type 6 42 | Enable 43 | Label 7 44 | Value 7 45 | Type 7 46 | Enable 47 | Label 8 48 | Value 8 49 | Type 8 50 | 51 | 52 | -------------------------------------------------------------------------------- /resources-eng/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | QR Code Viewer 3 | 4 | No QR code 5 | Select QR code 6 | 7 | Select QR Code 8 | 9 | Retain menu index 10 | Size 11 | Barcode height 12 | Offset Y 13 | Enable offline mode 14 | Error 15 | Please contact developer 16 | 17 | Display label 18 | Enable 19 | Label 1 20 | Value 1 21 | Type 1 22 | Enable 23 | Label 2 24 | Value 2 25 | Type 2 26 | Enable 27 | Label 3 28 | Value 3 29 | Type 3 30 | Enable 31 | Label 4 32 | Value 4 33 | Type 4 34 | Enable 35 | Label 5 36 | Value 5 37 | Type 5 38 | Enable 39 | Label 6 40 | Value 6 41 | Type 6 42 | Enable 43 | Label 7 44 | Value 7 45 | Type 7 46 | Enable 47 | Label 8 48 | Value 8 49 | Type 8 50 | 51 | 52 | -------------------------------------------------------------------------------- /resources-tha/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | QR Code Viewer 3 | 4 | ไม่พบ QR code 5 | เลือก QR code 6 | 7 | เลือก QR Code 8 | 9 | แสดงโค้ดล่าสุด 10 | ขนาด 11 | ค่าสูงของ Barcode 12 | เลื่อนภาพในแนวตั้ง 13 | ใช้งานแบบออฟไลน์ 14 | พบข้อผิดพลาด 15 | โทเคน หรือ ประเภทของโค้ดไม่ถูกต้อง 16 | 17 | แสดงชื่อ 18 | เปิดใช้งาน 19 | ชื่อ 1 20 | ค่า 1 21 | ประเภท 1 22 | เปิดใช้งาน 23 | ชื่อ 2 24 | ค่า 2 25 | ประเภท 2 26 | เปิดใช้งาน 27 | ชื่อ 3 28 | ค่า 3 29 | ประเภท 3 30 | เปิดใช้งาน 31 | ชื่อ 4 32 | ค่า 4 33 | ประเภท 4 34 | เปิดใช้งาน 35 | ชื่อ 5 36 | ค่า 5 37 | ประเภท 5 38 | เปิดใช้งาน 39 | ชื่อ 6 40 | ค่า 6 41 | ประเภท 6 42 | เปิดใช้งาน 43 | ชื่อ 7 44 | ค่า 7 45 | ประเภท 7 46 | เปิดใช้งาน 47 | ชื่อ 8 48 | ค่า 8 49 | ประเภท 8 50 | 51 | 52 | -------------------------------------------------------------------------------- /resources-ces/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | QR Code Viewer 3 | 4 | Žádný QR kód 5 | Vyberte QR kód 6 | 7 | Vyberte QR kód 8 | 9 | Ponechat index nabídky 10 | Velikost 11 | Výška èárového kódu 12 | Ofset Y 13 | Zapnout offline mód 14 | Chyba 15 | Prosím kontatujte vývojáøe 16 | 17 | Zobrazit popis 18 | Aktivovat 19 | Popis 1 20 | Hodnota 1 21 | Typ 1 22 | Aktivovat 23 | Popis 2 24 | Hodnota 2 25 | Typ 2 26 | Aktivovat 27 | Popis 3 28 | Hodnota 3 29 | Typ 3 30 | Aktivovat 31 | Popis 4 32 | Hodnota 4 33 | Typ 4 34 | Aktivovat 35 | Popis 5 36 | Hodnota 5 37 | Typ 5 38 | Aktivovat 39 | Popis 6 40 | Hodnota 6 41 | Typ 6 42 | Aktivovat 43 | Popis 7 44 | Hodnota 7 45 | Typ 7 46 | Aktivovat 47 | Popis 8 48 | Hodnota 8 49 | Typ 8 50 | 51 | -------------------------------------------------------------------------------- /resources-fre/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | QR Code Viewer 3 | 4 | Pas de QR code 5 | Sélectionnez un QR code 6 | 7 | QR Code 8 | 9 | Conserver l'index du menu 10 | Taille 11 | Hauteur des codes-barres 12 | Offset Y 13 | Mode déconnecté 14 | Erreur 15 | Veuillez contacter le développeur 16 | 17 | Affiche les libellés 18 | Actif 19 | Libellé 1 20 | Valeur 1 21 | Type 1 22 | Actif 23 | Libellé 2 24 | Valeur 2 25 | Type 2 26 | Actif 27 | Libellé 3 28 | Valeur 3 29 | Type 3 30 | Actif 31 | Libellé 4 32 | Valeur 4 33 | Type 4 34 | Actif 35 | Libellé 5 36 | Valeur 5 37 | Type 5 38 | Actif 39 | Libellé 6 40 | Valeur 6 41 | Type 6 42 | Actif 43 | Libellé 7 44 | Valeur 7 45 | Type 7 46 | Actif 47 | Libellé 8 48 | Valeur 8 49 | Type 8 50 | 51 | 52 | -------------------------------------------------------------------------------- /resources-deu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | QR-Code Viewer 3 | 4 | Kein QR-code 5 | Wähle QR-Code 6 | 7 | Wähle QR-Code 8 | 9 | Menüindex beibehalten 10 | Grösse 11 | Barcode-Höhe 12 | Offset Y 13 | Offline-Modus aktivieren 14 | Fehler 15 | Bitte Entwickler kontaktieren 16 | 17 | Displaybezeichnung 18 | Aktiv 19 | Bezeichnung 1 20 | Wert 1 21 | Typ 1 22 | Aktiv 23 | Bezeichnung 2 24 | Wert 2 25 | Typ 2 26 | Aktiv 27 | Bezeichnung 3 28 | Wert 3 29 | Typ 3 30 | Aktiv 31 | Bezeichnung 4 32 | Wert 4 33 | Typ 4 34 | Aktiv 35 | Bezeichnung 5 36 | Wert 5 37 | Typ 5 38 | Aktiv 39 | Bezeichnung 6 40 | Wert 6 41 | Typ 6 42 | Aktiv 43 | Bezeichnung 7 44 | Wert 7 45 | Typ 7 46 | Aktiv 47 | Bezeichnung 8 48 | Wert 8 49 | Typ 8 50 | 51 | 52 | -------------------------------------------------------------------------------- /resources/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | false 3 | false 4 | 5 | 6 | qrcode 7 | false 8 | 9 | 10 | qrcode 11 | false 12 | 13 | 14 | qrcode 15 | false 16 | 17 | 18 | qrcode 19 | false 20 | 21 | 22 | qrcode 23 | false 24 | 25 | 26 | qrcode 27 | false 28 | 29 | 30 | qrcode 31 | false 32 | 33 | 34 | qrcode 35 | false 36 | 0 37 | 0 38 | 0 39 | true 40 | -------------------------------------------------------------------------------- /source/Code.mc: -------------------------------------------------------------------------------- 1 | using Toybox.Application as App; 2 | 3 | class Code { 4 | var id; 5 | var enabled; 6 | var label; 7 | var type; 8 | var value; 9 | var cache; 10 | var lat; 11 | var lng; 12 | 13 | function initialize(id, enabled, label, type, value, cache, lat, lng) { 14 | self.id = id; 15 | self.enabled = enabled; 16 | self.label = label; 17 | self.type = type; 18 | self.value = value; 19 | self.cache = cache; 20 | self.lat = lat; 21 | self.lng = lng; 22 | if(isNullOrEmpty(self.type)) { 23 | self.type = "qrcode"; 24 | } 25 | } 26 | 27 | function store() { 28 | var app = App.getApp(); 29 | var id = self.id; 30 | System.println("Store code #" + id); 31 | System.println("Store code " + self); 32 | app.setProperty("codeEnable"+ id, self.enabled); 33 | app.setProperty("codeType" + id, self.type); 34 | app.setProperty("codeLabel" + id, self.label); 35 | app.setProperty("codeValue" + id, self.value); 36 | app.setProperty("cacheValue"+ id, self.value); 37 | app.setProperty("cacheData" + id, self.cache); 38 | app.setProperty("codeLat" + id, self.lat); 39 | app.setProperty("codeLng" + id, self.lng); 40 | } 41 | 42 | function fromSettings(id) { 43 | var app = App.getApp(); 44 | var value = app.getProperty("codeValue" + id); 45 | var cacheValue = app.getProperty("cacheValue"+ id); 46 | if(value == null || !value.equals(cacheValue)) { 47 | app.setProperty("cacheData"+ id, null); 48 | } 49 | System.println("Load code #" + id + " : " + app.getProperty("codeEnable"+ id)); 50 | return new Code( 51 | id, 52 | app.getProperty("codeEnable"+ id), 53 | app.getProperty("codeLabel" + id), 54 | app.getProperty("codeType" + id), 55 | app.getProperty("codeValue" + id), 56 | app.getProperty("cacheData" + id), 57 | app.getProperty("codeLat" + id), 58 | app.getProperty("codeLng" + id) 59 | ); 60 | } 61 | 62 | function fromResponseData(id, data) { 63 | return new Code( 64 | id, 65 | true, 66 | data["name"], 67 | data["type"], 68 | data["value"], 69 | data["encodedData"], 70 | data["latlng"]["lat"], 71 | data["latlng"]["lng"] 72 | ); 73 | } 74 | 75 | function toString() { 76 | return "Code{" 77 | + "id: " + self.id 78 | + ", enabled: " + self.enabled 79 | + ", label: " + self.label 80 | + ", type: " + self.type 81 | + ", value: " + self.value 82 | + ", cache: " + self.cache 83 | + "}"; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | ces 87 | deu 88 | eng 89 | fre 90 | tha 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /source/QRCodeViewerApp.mc: -------------------------------------------------------------------------------- 1 | using Toybox.WatchUi as Ui; 2 | using Toybox.Application as App; 3 | using Toybox.Communications as Comm; 4 | 5 | class QRCodeViewerApp extends App.AppBase { 6 | 7 | var enabledCodes = []; 8 | var loadingCache = 0; 9 | var latlng = null; 10 | var status = :UNKNOWN; 11 | 12 | //////////////////////////////////////////////////////////////// 13 | // Callbacks 14 | //////////////////////////////////////////////////////////////// 15 | 16 | function onReceive(responseCode, data) { 17 | System.println("Receiving data (" + responseCode + "): " + data); 18 | var app = App.getApp(); 19 | loadingCache--; 20 | if (responseCode == 200) { 21 | var id = data["id"]; 22 | app.setProperty("cacheValue" + id, data["data"]); 23 | app.setProperty("cacheData" + id, data["response"]); 24 | System.println("Cache data #" + id + " loaded"); 25 | } else { 26 | System.println("Error while loading data : " + responseCode); 27 | // nothing to do, data will be store next time 28 | } 29 | if(loadingCache==0) { 30 | Ui.requestUpdate(); 31 | } 32 | } 33 | 34 | //////////////////////////////////////////////////////////////// 35 | // Private methods 36 | //////////////////////////////////////////////////////////////// 37 | 38 | function getCodeIndex(id) { 39 | for(var i=0; i Comm.HTTP_REQUEST_METHOD_GET, 63 | :headers => { 64 | "Content-Type" => Comm.REQUEST_CONTENT_TYPE_JSON 65 | }, 66 | :responseType => Comm.HTTP_RESPONSE_CONTENT_TYPE_JSON 67 | }, 68 | method(:onReceive) 69 | ); 70 | } 71 | } 72 | 73 | function initQRCodeSettings(id) { 74 | var app = App.getApp(); 75 | var code = Code.fromSettings(id); 76 | System.println("Initialize code " + code); 77 | var cacheValue = code.cache; 78 | if(code.enabled && !isNullOrEmpty(code.label) && !isNullOrEmpty(code.value)) { 79 | // The QR Code exist, we have to handle with it 80 | if(code.value != null && code.cache == null) { 81 | loadQRCodeData(code); 82 | } 83 | System.println("Add QR code #" + id); 84 | enabledCodes.add(code); 85 | } else if(Settings.currentId == id) { 86 | System.println("Reset currentId"); 87 | Settings.setCurrentId(null); 88 | } else { 89 | System.println("Code not loaded"); 90 | } 91 | } 92 | 93 | function initQRCodes() { 94 | System.println("init QR codes..."); 95 | enabledCodes = []; 96 | for(var i=1; i<=8; i++) { 97 | initQRCodeSettings(i); 98 | } 99 | if(loadingCache==0) { 100 | setStatus(:READY); 101 | Ui.requestUpdate(); 102 | } 103 | } 104 | 105 | function handleSettings() { 106 | System.println("Handle settings..."); 107 | Settings.load(); 108 | System.println( 109 | "Settings = {" 110 | + "barcodeHeight: " + Settings.barcodeHeight 111 | + ", cacheEnabled: " + Settings.cacheEnabled 112 | + ", currentId: " + Settings.currentId 113 | + ", displayLabel: " + Settings.displayLabel 114 | + ", offsetY: " + Settings.offsetY 115 | + ", retainMenuIndex: " + Settings.retainMenuIndex 116 | + ", size: " + Settings.size 117 | + "}" 118 | ); 119 | 120 | var app = App.getApp(); 121 | initQRCodes(); 122 | } 123 | 124 | function setStatus(newStatus) { 125 | if(status == newStatus) { 126 | return; 127 | } 128 | System.println("set status : " + newStatus); 129 | status = newStatus; 130 | Ui.requestUpdate(); 131 | } 132 | 133 | function orderCodes() { 134 | } 135 | 136 | //////////////////////////////////////////////////////////////// 137 | // Public methods 138 | //////////////////////////////////////////////////////////////// 139 | 140 | function initialize() { 141 | System.println("App initialization..."); 142 | AppBase.initialize(); 143 | handleSettings(); 144 | System.println("App initialized."); 145 | } 146 | 147 | function onSettingsChanged() { 148 | AppBase.onSettingsChanged(); 149 | handleSettings(); 150 | } 151 | 152 | // onStart() is called on application start up 153 | function onStart(state) { 154 | System.println("onStart : " + state); 155 | } 156 | 157 | // onStop() is called when your application is exiting 158 | function onStop(state) { 159 | System.println("onStop : " + state); 160 | } 161 | 162 | // Return the initial view of your application here 163 | function getInitialView() { 164 | return [ new QRCodeViewerView(), new QRCodeViewerDelegate() ]; 165 | } 166 | 167 | } -------------------------------------------------------------------------------- /resources/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /source/menu/DMenu.mc: -------------------------------------------------------------------------------- 1 | using Toybox.WatchUi as Ui; 2 | using Toybox.Graphics as Gfx; 3 | 4 | var SCALE = 100; 5 | 6 | // Inherit from this if you want to store additional information in the menu entry and/or change how 7 | // the menu is drawn - for example adding in a status icon. 8 | // Any overridden drawing should be constrained within the items boundaries, i.e. y .. y + height / 3. 9 | class DMenuItem 10 | { 11 | const LABEL_FONT = Gfx.FONT_SMALL; 12 | const SELECTED_LABEL_FONT = Gfx.FONT_LARGE; 13 | const VALUE_FONT = Gfx.FONT_MEDIUM; 14 | const PAD = 0; 15 | 16 | var id, label, value, userData; 17 | var index; // filled in with its index, if selected 18 | 19 | // _id is typically a symbol but can be anything and is just used in menu delegate to identify 20 | // which item has been selected. 21 | // _label the text to show as the item name. Can be any object responding to toString (). 22 | // _value the text to show when the item is in the selectable position. Use null for no text 23 | // otherwise any object responding to toString () can be used. 24 | // _userData optional. 25 | function initialize (_id, _label, _value, _userData) 26 | { 27 | id = _id; 28 | label = _label; 29 | value = _value; 30 | userData = _userData; 31 | } 32 | 33 | function draw (dc, y, highlight) 34 | { 35 | 36 | if (highlight) 37 | { 38 | setHighlightColor (dc); 39 | drawHighlightedLabel (dc, y); 40 | } 41 | else 42 | { 43 | setColor (dc); 44 | drawLabel (dc, y); 45 | } 46 | } 47 | 48 | function setHighlightColor (dc) 49 | { 50 | dc.setColor (Gfx.COLOR_BLACK, Gfx.COLOR_WHITE); 51 | } 52 | 53 | function setColor (dc) 54 | { 55 | dc.setColor (Gfx.COLOR_BLACK, Gfx.COLOR_WHITE); 56 | } 57 | 58 | function drawLabel (dc, y) 59 | { 60 | var width = dc.getWidth (); 61 | var h3 = dc.getHeight () / 3; 62 | var lab = label.toString (); 63 | var labDims = dc.getTextDimensions (lab, LABEL_FONT); 64 | var yL = y + (h3 - labDims[1]) / 2; 65 | 66 | dc.drawText (width / 2, yL, LABEL_FONT, lab, Gfx.TEXT_JUSTIFY_CENTER); 67 | } 68 | 69 | function drawHighlightedLabel (dc, y) 70 | { 71 | var width = dc.getWidth (); 72 | var h3 = dc.getHeight () / 3; 73 | var lab = label.toString (); 74 | var labDims = dc.getTextDimensions (lab, SELECTED_LABEL_FONT); 75 | var yL, yV, h; 76 | 77 | if (value != null) 78 | { 79 | // Show label and value. 80 | var val = value.toString (); 81 | var index = val.find("\n"); 82 | if(index != null) { 83 | val = val.substring(0, index); 84 | } 85 | var valDims = dc.getTextDimensions (val, VALUE_FONT); 86 | 87 | h = labDims[1] + valDims[1] + PAD; 88 | yL = y + (h3 - h) / 2; 89 | yV = yL + labDims[1] + PAD; 90 | dc.drawText (width / 2, yV, VALUE_FONT, val, Gfx.TEXT_JUSTIFY_CENTER); 91 | } 92 | else 93 | { 94 | yL = y + (h3 - labDims[1]) / 2; 95 | } 96 | dc.drawText (width / 2, yL, SELECTED_LABEL_FONT, lab, Gfx.TEXT_JUSTIFY_CENTER); 97 | } 98 | } 99 | 100 | class DMenu extends Ui.View 101 | { 102 | var menuArray; 103 | var title; 104 | var index; 105 | 106 | var nextIndex; 107 | hidden var drawMenu; 108 | 109 | function initialize (_menuArray, _menuTitle) 110 | { 111 | menuArray = _menuArray; 112 | title = _menuTitle; 113 | index = 0; 114 | nextIndex = 0; 115 | 116 | View.initialize (); 117 | } 118 | 119 | function onShow () 120 | { 121 | drawMenu = new DrawMenu (); 122 | } 123 | 124 | function onHide () 125 | { 126 | drawMenu = null; 127 | } 128 | 129 | // Return the menuItem with the matching id. The menu item has its index field updated 130 | // with the index it was found at. Returns null if not found. 131 | function itemWithId (id) 132 | { 133 | for (var idx = 0; idx < menuArray.size (); idx++) 134 | { 135 | if (menuArray[idx].id == id) 136 | { 137 | menuArray[idx].index = idx; 138 | return menuArray[idx]; 139 | } 140 | } 141 | return null; 142 | } 143 | 144 | const ANIM_TIME = 0.3; 145 | function updateIndex (offset) 146 | { 147 | if (menuArray.size () <= 1) 148 | { 149 | return; 150 | } 151 | 152 | /* 153 | if (offset == 1) 154 | { 155 | // Scroll down. Use 1000 as end value as cannot use 1. Scale as necessary in draw call. 156 | Ui.animate (drawMenu, :t, Ui.ANIM_TYPE_LINEAR, SCALE, 0, ANIM_TIME, null); 157 | } 158 | else 159 | { 160 | // Scroll up. 161 | Ui.animate (drawMenu, :t, Ui.ANIM_TYPE_LINEAR, -SCALE, 0, ANIM_TIME, null); 162 | } 163 | //*/ 164 | nextIndex = index + offset; 165 | 166 | // Cope with a 'feature' in modulo operator not handling -ve numbers as desired. 167 | nextIndex = nextIndex < 0 ? menuArray.size () + nextIndex : nextIndex; 168 | 169 | nextIndex = nextIndex % menuArray.size (); 170 | 171 | Ui.requestUpdate(); 172 | index = nextIndex; 173 | } 174 | 175 | function selectedItem () 176 | { 177 | menuArray[index].index = index; 178 | return menuArray[index]; 179 | } 180 | 181 | function onUpdate (dc) 182 | { 183 | if(drawMenu == null) { 184 | return; 185 | } 186 | var width = dc.getWidth (); 187 | var height = dc.getHeight (); 188 | 189 | dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK); 190 | dc.fillRectangle(0, 0, width, height); 191 | 192 | // Draw the menu items. 193 | drawMenu.index = index; 194 | drawMenu.nextIndex = nextIndex; 195 | drawMenu.menu = self; 196 | 197 | drawMenu.draw (dc); 198 | 199 | // Draw the decorations. 200 | var h3 = height / 3; 201 | dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_WHITE); 202 | dc.setPenWidth (2); 203 | dc.drawLine (0, h3, width, h3); 204 | dc.drawLine (0, h3 * 2, width, h3 * 2); 205 | 206 | drawArrows (dc); 207 | } 208 | 209 | const GAP = 5; 210 | const TS = 5; 211 | 212 | // The arrows are drawn with lines as polygons don't give different sized triangles depending 213 | // on their orientation. 214 | function drawArrows (dc) 215 | { 216 | var x = dc.getWidth () / 2; 217 | var y; 218 | 219 | dc.setPenWidth (1); 220 | dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_WHITE); 221 | 222 | if (nextIndex != 0) 223 | { 224 | y = GAP; 225 | 226 | for (var i = 0; i < TS; i++) 227 | { 228 | dc.drawLine (x - i, y + i, x + i + 1, y + i); 229 | } 230 | } 231 | 232 | if (nextIndex != menuArray.size () - 1) 233 | { 234 | y = dc.getHeight () - TS - GAP; 235 | 236 | var d; 237 | for (var i = 0; i < TS; i++) 238 | { 239 | d = TS - 1 - i; 240 | dc.drawLine (x - d, y + i, x + d + 1, y + i); 241 | } 242 | } 243 | } 244 | } 245 | 246 | // Done as a class so it can be animated. 247 | class DrawMenu extends Ui.Drawable 248 | { 249 | const TITLE_FONT = Gfx.FONT_SMALL; 250 | 251 | var t = 0; // 'time' in the animation cycle 0...1000 or -1000...0. 252 | var index, nextIndex, menu; 253 | 254 | function initialize () 255 | { 256 | Drawable.initialize ({}); 257 | } 258 | 259 | function draw (dc) 260 | { 261 | var width = dc.getWidth (); 262 | var height = dc.getHeight (); 263 | var h3 = height / 3; 264 | var items = menu.menuArray.size (); 265 | 266 | nextIndex = menu.nextIndex; 267 | 268 | // y for the middle of the three items. 269 | var y = h3 + (t / SCALE) * h3; 270 | 271 | // Depending on where we are in the menu and in the animation some of 272 | // these will be unnecessary but it is easier to draw everything and 273 | // rely on clipping to avoid unnecessary drawing calls. 274 | drawTitle (dc, y - nextIndex * h3 - h3); 275 | for (var i = -2; i < 3; i++) 276 | { 277 | drawItem (dc, nextIndex + i, y + h3 * i, i == 0); 278 | } 279 | } 280 | 281 | function drawTitle (dc, y) 282 | { 283 | var width = dc.getWidth (); 284 | var h3 = dc.getHeight () / 3; 285 | 286 | // Check if any of the title is visible., 287 | if (y < -h3) 288 | { 289 | return; 290 | } 291 | 292 | dc.setColor (Gfx.COLOR_BLACK, Gfx.COLOR_WHITE); 293 | dc.fillRectangle (0, y, width, h3); 294 | 295 | if (menu.title != null) 296 | { 297 | var dims = dc.getTextDimensions (menu.title, TITLE_FONT); 298 | var h = (h3 - dims[1]) / 2; 299 | dc.setColor (Gfx.COLOR_WHITE, Gfx.COLOR_BLACK); 300 | dc.drawText (width / 2, y + h, TITLE_FONT, menu.title, Gfx.TEXT_JUSTIFY_CENTER); 301 | } 302 | } 303 | 304 | // highlight is the selected menu item that can optionally show a value. 305 | function drawItem (dc, idx, y, highlight) 306 | { 307 | var h3 = dc.getHeight () / 3; 308 | 309 | // Cannot see item if it doesn't exist or will not be visible. 310 | if (idx < 0 || idx >= menu.menuArray.size () || 311 | menu.menuArray[idx] == null || y > dc.getHeight () || y < -h3) 312 | { 313 | return; 314 | } 315 | 316 | menu.menuArray[idx].draw (dc, y, highlight); 317 | } 318 | } 319 | 320 | class DMenuDelegate extends Ui.BehaviorDelegate 321 | { 322 | hidden var menu; 323 | hidden var userMenuDelegate; 324 | 325 | function initialize (_menu, _userMenuInputDelegate) 326 | { 327 | menu = _menu; 328 | userMenuDelegate = _userMenuInputDelegate; 329 | BehaviorDelegate.initialize (); 330 | } 331 | 332 | function onNextPage () 333 | { 334 | menu.updateIndex (1); 335 | return true; 336 | } 337 | 338 | function onPreviousPage () 339 | { 340 | menu.updateIndex (-1); 341 | return true; 342 | } 343 | 344 | function onKey(e) { 345 | switch(e.getKey()) { 346 | case KEY_ENTER: 347 | case KEY_START: 348 | case KEY_MENU: 349 | return onNextPage(); 350 | case KEY_LAP: 351 | return onPreviousPage(); 352 | } 353 | return false; 354 | } 355 | 356 | function onSelect () 357 | { 358 | userMenuDelegate.onMenuItem (menu.selectedItem ()); 359 | Ui.requestUpdate(); 360 | return true; 361 | } 362 | 363 | function onBack () 364 | { 365 | Ui.popView (Ui.SLIDE_RIGHT); 366 | return true; 367 | } 368 | } -------------------------------------------------------------------------------- /source/QRCodeViewerView.mc: -------------------------------------------------------------------------------- 1 | using Toybox.WatchUi as Ui; 2 | using Toybox.Application as App; 3 | using Toybox.Communications as Comm; 4 | using Toybox.Graphics as Gfx; 5 | 6 | class QRCodeViewerView extends Ui.View { 7 | 8 | var qrCodeFont = []; 9 | var dcWidth = 0; 10 | var dcHeight = 0; 11 | var maxWidth = 0; 12 | var maxHeight = 0; 13 | var offsetHeight = 0; 14 | var size = 0; 15 | 16 | var requestCounter = 0; 17 | var image = null; 18 | var message = null; 19 | 20 | // Set up the responseCallback function to return an image or null 21 | function onReceiveImage(responseCode, data) { 22 | requestCounter--; 23 | System.println("Receiving image data (" + responseCode + "). Remaining " + requestCounter); 24 | if(requestCounter==0) { // handle only the last request 25 | if (responseCode == 200) { 26 | App.getApp().setStatus(:READY); 27 | System.println("QR code image loaded"); 28 | image = data; 29 | } else { 30 | App.getApp().setStatus(:ERROR); 31 | image = null; 32 | var app = App.getApp(); 33 | message = Ui.loadResource(Rez.Strings.error) +": " + responseCode 34 | + "\n" + Ui.loadResource(Rez.Strings.errorImage); 35 | System.println(message); 36 | } 37 | Ui.requestUpdate(); 38 | } 39 | } 40 | 41 | function initialize() { 42 | System.println("View initialization..."); 43 | View.initialize(); 44 | } 45 | 46 | // Load your resources here 47 | function onLayout(dc) { 48 | System.println("Loading resources..."); 49 | dcWidth = dc.getWidth(); 50 | dcHeight = dc.getHeight(); 51 | 52 | qrCodeFont = [ 53 | Ui.loadResource(Rez.Fonts.qrcode1), 54 | Ui.loadResource(Rez.Fonts.qrcode2), 55 | Ui.loadResource(Rez.Fonts.qrcode3), 56 | Ui.loadResource(Rez.Fonts.qrcode4), 57 | Ui.loadResource(Rez.Fonts.qrcode5), 58 | Ui.loadResource(Rez.Fonts.qrcode6), 59 | Ui.loadResource(Rez.Fonts.qrcode7), 60 | Ui.loadResource(Rez.Fonts.qrcode8), 61 | Ui.loadResource(Rez.Fonts.qrcode9), 62 | Ui.loadResource(Rez.Fonts.qrcode10), 63 | Ui.loadResource(Rez.Fonts.qrcode11), 64 | Ui.loadResource(Rez.Fonts.qrcode12), 65 | Ui.loadResource(Rez.Fonts.qrcode13), 66 | Ui.loadResource(Rez.Fonts.qrcode14), 67 | Ui.loadResource(Rez.Fonts.qrcode15), 68 | Ui.loadResource(Rez.Fonts.qrcode16) 69 | ]; 70 | System.println("resources loaded."); 71 | } 72 | 73 | // Called when this View is brought to the foreground. Restore 74 | // the state of this View and prepare it to be shown. This includes 75 | // loading resources into memory. 76 | function onShow() { 77 | System.println("View.onShow"); 78 | var app = App.getApp(); 79 | var id = Settings.currentId; 80 | if(id == null) { 81 | // nothing to show... 82 | System.println("View.onShow - nothing to show"); 83 | return; 84 | } 85 | 86 | maxWidth = dcWidth * 0.8; 87 | maxHeight= dcHeight * 0.8; 88 | if(maxWidth == maxHeight) { 89 | // For round device... Otherwise image is hidden in corner 90 | maxWidth = maxWidth * 0.8; 91 | maxHeight = maxHeight * 0.8; 92 | } 93 | 94 | if(Settings.displayLabel) { 95 | var fontHeight = Gfx.getFontHeight(Gfx.FONT_MEDIUM); 96 | var marginTop = (dcHeight - maxHeight) / 2; 97 | if(marginTop < fontHeight) { 98 | offsetHeight = fontHeight - marginTop; 99 | maxHeight = maxHeight - offsetHeight; 100 | } 101 | } 102 | 103 | size = Settings.size; 104 | if(size == 0) { 105 | size = maxWidth size, 130 | :maxHeight=> size 131 | }, 132 | method(:onReceiveImage) 133 | ); 134 | app.setStatus(:WAITING_CODES); 135 | } 136 | System.println("View.onShow - end"); 137 | } 138 | 139 | // Update the view 140 | function onUpdate(dc) { 141 | System.println("View.onUpdate"); 142 | // Call the parent onUpdate function to redraw the layout 143 | View.onUpdate(dc); 144 | 145 | var app = App.getApp(); 146 | var id = Settings.currentId; 147 | var data = getCachedData(id); 148 | if(id == null || message == null) { 149 | if(app.enabledCodes.size() == 0) { 150 | message = Ui.loadResource(Rez.Strings.errorNoQRCode); 151 | } else { 152 | message = Ui.loadResource(Rez.Strings.selectQRCode); 153 | } 154 | } 155 | dc.setColor (Gfx.COLOR_WHITE, Gfx.COLOR_BLACK); 156 | dc.clear(); 157 | dc.drawText( 158 | (dc.getWidth()) / 2, 159 | (dc.getHeight()) / 2, 160 | Gfx.FONT_MEDIUM, 161 | message, 162 | Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER 163 | ); 164 | if(id != null && (data != null || image != null)) { 165 | System.println("Display QR code"); 166 | var error = null; 167 | var imageFontSize = 1; 168 | if(data != null) { 169 | // On round watch barcode can be bigger 170 | var factor = (maxHeight==maxWidth && data.size()==1) ? 0.8 : 1; 171 | for(imageFontSize = 1; 172 | imageFontSize < qrCodeFont.size() && 173 | (imageFontSize+1) * data[0].length() <= size/factor+0.001; 174 | imageFontSize++ 175 | ) { 176 | } 177 | System.println("Code will be displayed using font size " + imageFontSize); 178 | } 179 | dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_WHITE); 180 | dc.clear(); 181 | if(Settings.displayLabel) { 182 | System.println("Display label"); 183 | dc.setColor (Gfx.COLOR_BLACK, Gfx.COLOR_WHITE); 184 | dc.drawText( 185 | (dc.getWidth()) / 2, 186 | offsetHeight + app.getProperty("offsetY") - 3, 187 | Gfx.FONT_MEDIUM, 188 | app.getProperty("codeLabel" + id), 189 | Gfx.TEXT_JUSTIFY_CENTER 190 | ); 191 | } 192 | if(data != null) { 193 | System.println("Display cached code"); 194 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK); 195 | drawQRCode(dc, data, imageFontSize); 196 | app.setStatus(:READY); 197 | } else { 198 | System.println("Display code image"); 199 | dc.drawBitmap( 200 | (dc.getWidth() - image.getWidth() ) / 2, 201 | (dc.getHeight() - image.getHeight()) / 2 + offsetHeight + app.getProperty("offsetY"), 202 | image 203 | ); 204 | } 205 | } else { 206 | switch(app.status) { 207 | case :READY: 208 | app.setStatus(:ERROR); 209 | case :WAITING_CODES: 210 | case :ERROR: 211 | case :UNKNOWN: 212 | break; 213 | default: 214 | System.println("Unknown status : " + app.status); 215 | app.setStatus(:UNKNOWN); 216 | } 217 | } 218 | updateStatus(dc); 219 | System.println("View updated."); 220 | } 221 | 222 | // Called when this View is removed from the screen. Save the 223 | // state of this View here. This includes freeing resources from 224 | // memory. 225 | function onHide() { 226 | } 227 | 228 | function updateStatus(dc) { 229 | var status = App.getApp().status; 230 | var color = Gfx.COLOR_RED; 231 | switch(status) { 232 | case :READY: 233 | System.println("status READY"); 234 | return false; 235 | case :WAITING_CODES: 236 | System.println("status WAITING_CODES"); 237 | color = Gfx.COLOR_BLUE; 238 | break; 239 | case :UNKNOWN: 240 | System.println("status UNKNOWN"); 241 | color = Gfx.COLOR_ORANGE; 242 | break; 243 | case :ERROR: 244 | System.println("status ERROR"); 245 | color = Gfx.COLOR_RED; 246 | break; 247 | default: 248 | System.println("Other status : " + status); 249 | color = COLOR_LT_GRAY; 250 | } 251 | dc.setColor(color, Gfx.COLOR_BLACK); 252 | dc.setPenWidth(dcHeight * 0.05); 253 | dc.drawRectangle(0, 0, dcWidth, dcHeight); 254 | return true; 255 | } 256 | 257 | function drawQRCode(dc, datas, moduleSize) { 258 | if(!(datas instanceof Toybox.Lang.Array)) { 259 | return; 260 | } 261 | var app = App.getApp(); 262 | var nbLines = datas.size(); 263 | if(nbLines == 1) { 264 | var barcodeHeight = Settings.barcodeHeight; 265 | if(barcodeHeight == 0) { 266 | barcodeHeight = dc.getHeight()/10; 267 | } 268 | nbLines = barcodeHeight / moduleSize; 269 | } 270 | var offsetY = (dc.getHeight() - (nbLines-1) * 4 * moduleSize) / 2 + offsetHeight + Settings.offsetY; 271 | for(var i=0; i