diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/app/Fixtures.mc | 13 | ||||
| -rw-r--r-- | source/ui/CodeView.mc | 84 |
2 files changed, 66 insertions, 31 deletions
diff --git a/source/app/Fixtures.mc b/source/app/Fixtures.mc index 0aa6ea9..0d6b683 100644 --- a/source/app/Fixtures.mc +++ b/source/app/Fixtures.mc @@ -61,6 +61,8 @@ module Fixtures { qr.payload = Bytes.fromUtf8("https://example.com/member/fake-0042?name=Alice%20Example&card=0000111122223333&valid=2027-12-31&level=gold"); qr.options = Codes.DEFAULT_OPTIONS; vault.add(qr); + vault.add(code("Parcel", Codes.DATAMATRIX, "PARCEL-FAKE-0042-7731")); + vault.add(code("Ticket", Codes.PDF417, "Boarding pass FAKE 0042 seat 12A")); vault.lock(); if (TestMode.ON) { startImport(); @@ -99,6 +101,17 @@ module Fixtures { + "\", \"info\": {\"secret\": \"" + secret + "\", \"algo\": \"SHA1\", \"digits\": " + digits + ", \"period\": 30}}"; } + function code(name as String, symbology as Number, payload as String) as Entry { + var e = new Entry(); + e.kind = Entry.KIND_CODE; + e.type = symbology; + e.name = name; + e.group = "Personal"; + e.payload = Bytes.fromUtf8(payload); + e.options = Codes.DEFAULT_OPTIONS; + return e; + } + function otp(name as String, account as String, group as String, type as Number, algorithm as Number, digits as Number, period as Number, secret as String, locked as Boolean) as Entry { var e = new Entry(); diff --git a/source/ui/CodeView.mc b/source/ui/CodeView.mc index 2687d6c..88de402 100644 --- a/source/ui/CodeView.mc +++ b/source/ui/CodeView.mc @@ -8,8 +8,9 @@ // whole number of pixels that fits the code plus 10 quiet modules on // each side; 1 px is allowed for linear codes (EAN-13 on 218 px screens // needs it, and the screenshot decode test covers it). -// * 2D codes use the largest centred square (round screens) or the -// screen's shorter side, at least 2 px per module. +// * 2D codes use the largest module size at which the code fits inside the +// circle (round screens) or the screen (Codes.fits): at least 2 px for +// square codes, 1 px for PDF417 (a stacked linear code). // * 2D codes are encoded once, as a job on a timer ("Encoding... 40%"), // and cached sealed with the entry's key (Vault.saveCodeCache), so the // next opening is instant. @@ -30,16 +31,17 @@ class CodeView extends WatchUi.View { static const QUIET_MODULES = 10; static const MIN_LINEAR_PX = 1; static const MIN_MATRIX_PX = 2; - static const QR_QUIET = 4; var header as Entry; private var _code as Codes.LinearCode or Codes.Matrix or Codes.CodeError or Null = null; private var _showText as Boolean = true; private var _shownLogged as Boolean = false; - private var _job as Qr.QrJob? = null; + private var _job as Codes.MatrixJob? = null; private var _runner as Jobs.JobRunner? = null; private var _ticker as Timer.Timer? = null; private var _startedAt as Number = 0; + // The cache-saving one-shot timer (kept so it is not collected). + var saveTimer as Timer.Timer? = null; function initialize(entryHeader as Entry) { View.initialize(); @@ -73,8 +75,14 @@ class CodeView extends WatchUi.View { _showText = (entry.options & Codes.OPT_SHOW_TEXT) != 0; if (payload == null) { _code = new Codes.CodeError("damaged text"); - } else if (entry.type == Codes.QR) { - startQr(payload, Codes.ecLevel(entry.options)); + } else if (Codes.isMatrix(entry.type)) { + var screen = System.getDeviceSettings(); + var job = Codes.matrixJob(entry.type, payload, entry.options, screen.screenWidth, screen.screenHeight, Ui.isRound()); + if (job != null) { + startJob(job); + } else { + _code = new Codes.CodeError(Codes.name(entry.type) + " is not supported yet"); + } } else { _code = Codes.encode(entry.type, payload, entry.options); } @@ -84,9 +92,8 @@ class CodeView extends WatchUi.View { } } - private function startQr(payload as String, ec as Number) as Void { + private function startJob(job as Codes.MatrixJob) as Void { _startedAt = System.getTimer(); - var job = new Qr.QrJob(payload, ec, 0, -1); _job = job; var runner = new Jobs.JobRunner(job, 1, method(:onEncoded)); _runner = runner; @@ -107,22 +114,36 @@ class CodeView extends WatchUi.View { return; } var err = job.error; - var result = job.result; - if (err != null || result == null) { + var matrix = job.matrix(); + if (err != null || matrix == null) { _code = new Codes.CodeError(err != null ? err : "could not encode"); } else { - Diag.data("qr_encoded", "version=" + result.version + " ms=" + (System.getTimer() - _startedAt)); - var matrix = new Codes.Matrix(result.size, result.size, QR_QUIET, result.modules); + Diag.data("matrix_encoded", "type=" + header.type + " size=" + matrix.width + "x" + matrix.height + + " ms=" + (System.getTimer() - _startedAt)); _code = matrix; - try { - AppState.theVault().saveCodeCache(header.id, matrix.toBytes()); - } catch (e instanceof VaultError) { - // Locked meanwhile: no cache this time. - } + // Sealing and storing the cache gets its own timer event: the + // job's last step already used much of this event's budget. + var t = new Timer.Timer(); + t.start(method(:saveCache), 50, false); + saveTimer = t; } WatchUi.requestUpdate(); } + // Store the encoded matrix sealed in the vault (one-shot timer). + function saveCache() as Void { + saveTimer = null; + var code = _code; + if (!(code instanceof Codes.Matrix)) { + return; + } + try { + AppState.theVault().saveCodeCache(header.id, code.toBytes()); + } catch (e instanceof VaultError) { + // Locked meanwhile: no cache this time. + } + } + private function stopJob() as Void { var runner = _runner; if (runner != null) { @@ -229,27 +250,28 @@ class CodeView extends WatchUi.View { } } - // A 2D code in the largest centred square (or rectangle) that fits. + // A 2D code at the largest module size that fits (Codes.fits: inside the + // circle on round screens), centred. private function drawMatrix(dc as Graphics.Dc, code as Codes.Matrix, font as Graphics.FontType, fontH as Number) as Void { var w = dc.getWidth(); var h = dc.getHeight(); - // Round screens: the largest square inside the circle (side w/sqrt 2); - // others: the shorter side, minus room for the name and ID lines. - var areaW = Ui.isRound() ? w * 707 / 1000 : w - 4; - var areaH = Ui.isRound() ? h * 707 / 1000 : h - 2 * fontH - 8; var totalW = code.width + 2 * code.quiet; - var totalH = code.height + 2 * code.quiet; - var px = areaW / totalW; - if (areaH / totalH < px) { - px = areaH / totalH; + var totalH = code.drawnHeight() + 2 * code.quiet; + var minPx = Codes.minModulePx(header.type); + var px = 0; + for (var p = 16; p >= minPx && px == 0; p--) { + if (Codes.fits(totalW, totalH, p, w, h, Ui.isRound())) { + px = p; + } } - if (px < MIN_MATRIX_PX) { - drawMessage(dc, "Too large for this screen: needs " + MIN_MATRIX_PX + " px per module, a " - + totalW * MIN_MATRIX_PX + " px square", font, fontH); + if (px == 0) { + drawMessage(dc, "Too large for this screen: needs " + minPx + " px per module, " + totalW * minPx + " x " + + totalH * minPx + " px", font, fontH); return; } var x0 = (w - code.width * px) / 2; - var y0 = (h - code.height * px) / 2; + var y0 = (h - code.drawnHeight() * px) / 2; + var rowPx = code.rowHeight * px; dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_WHITE); var m = code.modules; for (var y = 0; y < code.height; y++) { @@ -261,7 +283,7 @@ class CodeView extends WatchUi.View { while (x < code.width && m[row + x] == 1) { x++; } - dc.fillRectangle(x0 + start * px, y0 + y * px, (x - start) * px, px); + dc.fillRectangle(x0 + start * px, y0 + y * rowPx, (x - start) * px, rowPx); } else { x++; } |
