summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcos <cos>2020-03-14 03:51:05 +0100
committercos <cos>2022-05-14 12:52:46 +0200
commit2cb6237ba1751725c660315dace2e0d879f64332 (patch)
treefca9035c84d6f03ee03b7d84e16bd1ee2f94f557
parentfbac0449b785ef865ee4727aa6cdc2b39315dddb (diff)
downloadmazarine-watch-2cb6237ba1751725c660315dace2e0d879f64332.zip
First initial version of watchface
-rw-r--r--.project17
-rw-r--r--LICENSE2
-rw-r--r--Makefile33
-rw-r--r--README.md39
-rw-r--r--manifest.xml81
-rw-r--r--resources/properties.xml7
-rw-r--r--resources/settings.xml11
-rw-r--r--resources/strings.xml7
-rw-r--r--source/CopernicusApp.mc38
-rw-r--r--source/CopernicusView.mc397
-rw-r--r--source/MazarineApp.mc28
-rw-r--r--source/MazarineView.mc392
12 files changed, 507 insertions, 545 deletions
diff --git a/.project b/.project
deleted file mode 100644
index a4cc0fd..0000000
--- a/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>Copernicus</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>connectiq.builder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>connectiq.projectNature</nature>
- </natures>
-</projectDescription>
diff --git a/LICENSE b/LICENSE
index 6f33ac8..37c7f6a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (C) 2019 cy384
+Copyright (C) 2019 cy384, 2020 cos
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2f80eb5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,33 @@
+DEVICE=vivoactive3
+SDK=3.1.7
+VERSION="0.0.1"
+OUTPUT=bin/mazarine_$(VERSION).prg
+MONKEY_SDK=~/CONNECTIQ
+CONNIQ=$(MONKEY_SDK)/bin/connectiq
+MONKEYC=$(MONKEY_SDK)/bin/monkeyc
+MONKEYDO=$(MONKEY_SDK)/bin/monkeydo
+PRIVATE_KEY=~/developer_key.der
+MANIFEST=manifest.xml
+RESOURCES := $(shell find resources/ -name "*.xml" -exec echo -n {}\ \; | tr " " ":")
+MDO := $(shell ps | grep -v grep | grep MonkeyDoDeux | awk '{print $1}')
+
+all: build run
+
+build:
+ $(MONKEYC) \
+ -o $(OUTPUT) \
+ -w \
+ -y $(PRIVATE_KEY) \
+ -z $(RESOURCES) \
+ -m $(MANIFEST) \
+ source/*.mc \
+ -d $(DEVICE) \
+ -s $(SDK)
+
+run:
+ #[ ! -z "$(MOD)" ] && kill -9 $(MOD)
+ # $(CONNIQ)
+ $(MONKEYDO) $(OUTPUT) $(DEVICE) &
+
+dev:
+ notifyloop . make build run
diff --git a/README.md b/README.md
index 5782655..8b89abc 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,12 @@
-Copernicus
-==========
-![Copernicus](https://i.imgur.com/UHR9TMi.png)
-
-A digital homage to the Raketa Kopernik mechanical watch, inspired by the orbits of the sun and the moon, with a few configurable features:
-
-* Dark and light variants
-
-* Enable/disable arbor
-
-* Enable/disable seconds hand
-
-The design is high-contrast and intended to be easily readable with or without the backlight active.
-
-It may be found [on the Garmin store](https://apps.garmin.com/en-US/apps/7f9c1277-ffb8-44f6-ab86-963ad88c85a6).
-
-Building
---------
-Follow the Garmin instructions for installing and configuring the SDK, along with Eclipse. Note the key requirements, etc. Unfortunately, you cannot, as far as I can tell, build the bundle necessary to submit to the Garmin store without using their Eclipse plugins (also the SDK tools don't seem to run on Ubuntu without fiddling anymore?). This version builds with the ConnectIQ SDK version `3.0.11`.
-
-After you do a build, you can deploy it locally by just copying the `.prg` file over USB to your watch.
-
-Open Source
------------
-I'm releasing this as open source in the hopes that someone may find it interesting or useful. The only mildly clever feature in the code is that things drawn on-screen are scaled to the screen size in-code for each device. This lets the watchface port trivially to all Garmin watches, regardless of shape and pixel count.
-
-This code is covered by the included BSD 3-clause license (see LICENSE file). Any patches, bug reports, or other user commments are greatly appreciated. I will note that I personally consider this feature-complete, and am not likely to merge features that make the design more complicated. Please feel free to fork if you plan on signficant additions.
+Mazarine
+========
+A watchface for my personal needs. Based on [Copernicus by cy384][0] which has
+a more approachable code-base. Essentially everything but the core has been
+replaced.
+
+Watchface names are practically never visible and naming is hard. Thus this
+name has no deeper meaning. In some sense it can be seen as a tribute to
+[Mazarine Street][1], the band I listened to while coding this.
+
+[0]: https://github.com/cy384/copernicus,
+[1]: https://sv.wikipedia.org/wiki/Mazarine_Street
diff --git a/manifest.xml b/manifest.xml
index 847cefd..39df2e7 100644
--- a/manifest.xml
+++ b/manifest.xml
@@ -1,41 +1,40 @@
-<!-- This is a generated file. It is highly recommended that you DO NOT edit this file. --><iq:manifest xmlns:iq="http://www.garmin.com/xml/connectiq" version="3">
- <iq:application entry="CopernicusApp" id="b3759b67-2fc3-41ea-8e15-a4b501e223a8" launcherIcon="@Drawables.LauncherIcon" minSdkVersion="1.2.0" name="@Strings.AppName" type="watchface" version="1.0.0">
- <iq:products>
- <iq:product id="approachs60"/>
- <iq:product id="d2bravo"/>
- <iq:product id="d2bravo_titanium"/>
- <iq:product id="d2charlie"/>
- <iq:product id="d2delta"/>
- <iq:product id="d2deltapx"/>
- <iq:product id="d2deltas"/>
- <iq:product id="descentmk1"/>
- <iq:product id="epix"/>
- <iq:product id="fenix3"/>
- <iq:product id="fenix3_hr"/>
- <iq:product id="fenix5"/>
- <iq:product id="fenix5plus"/>
- <iq:product id="fenix5s"/>
- <iq:product id="fenix5splus"/>
- <iq:product id="fenix5x"/>
- <iq:product id="fenix5xplus"/>
- <iq:product id="fenixchronos"/>
- <iq:product id="fr230"/>
- <iq:product id="fr235"/>
- <iq:product id="fr630"/>
- <iq:product id="fr645"/>
- <iq:product id="fr645m"/>
- <iq:product id="fr735xt"/>
- <iq:product id="fr920xt"/>
- <iq:product id="fr935"/>
- <iq:product id="vivoactive"/>
- <iq:product id="vivoactive3"/>
- <iq:product id="vivoactive3m"/>
- <iq:product id="vivoactive_hr"/>
- </iq:products>
- <iq:permissions/>
- <iq:languages>
- <iq:language>eng</iq:language>
- </iq:languages>
- <iq:barrels/>
- </iq:application>
-</iq:manifest>
+<iq:manifest xmlns:iq="http://www.garmin.com/xml/connectiq" version="3">
+ <iq:application entry="MazarineApp" id="b3759b67-2fc3-41ea-8e15-a4b501e223a8" launcherIcon="@Drawables.LauncherIcon" minSdkVersion="1.2.0" name="@Strings.AppName" type="watchface" version="0.0.1">
+ <iq:products>
+ <iq:product id="approachs60"/>
+ <iq:product id="d2bravo"/>
+ <iq:product id="d2bravo_titanium"/>
+ <iq:product id="d2charlie"/>
+ <iq:product id="d2delta"/>
+ <iq:product id="d2deltapx"/>
+ <iq:product id="d2deltas"/>
+ <iq:product id="descentmk1"/>
+ <iq:product id="epix"/>
+ <iq:product id="fenix3"/>
+ <iq:product id="fenix3_hr"/>
+ <iq:product id="fenix5"/>
+ <iq:product id="fenix5plus"/>
+ <iq:product id="fenix5s"/>
+ <iq:product id="fenix5splus"/>
+ <iq:product id="fenix5x"/>
+ <iq:product id="fenix5xplus"/>
+ <iq:product id="fenixchronos"/>
+ <iq:product id="fr230"/>
+ <iq:product id="fr235"/>
+ <iq:product id="fr630"/>
+ <iq:product id="fr645"/>
+ <iq:product id="fr645m"/>
+ <iq:product id="fr735xt"/>
+ <iq:product id="fr920xt"/>
+ <iq:product id="fr935"/>
+ <iq:product id="vivoactive"/>
+ <iq:product id="vivoactive3"/>
+ <iq:product id="vivoactive3m"/>
+ <iq:product id="vivoactive_hr"/>
+ </iq:products>
+ <iq:languages>
+ <iq:language>eng</iq:language>
+ </iq:languages>
+ <iq:barrels/>
+ </iq:application>
+</iq:manifest>
diff --git a/resources/properties.xml b/resources/properties.xml
deleted file mode 100644
index fd9cb4a..0000000
--- a/resources/properties.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-<properties>
- <property id="display_dark" type="boolean">true</property>
- <property id="display_seconds" type="boolean">true</property>
- <property id="display_arbor" type="boolean">true</property>
-</properties>
-
diff --git a/resources/settings.xml b/resources/settings.xml
deleted file mode 100644
index e17851e..0000000
--- a/resources/settings.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<settings>
-<setting propertyKey="@Properties.display_dark" title="@Strings.display_dark_title">
-<settingConfig type="boolean" />
-</setting>
-<setting propertyKey="@Properties.display_seconds" title="@Strings.display_seconds_title">
-<settingConfig type="boolean" />
-</setting>
-<setting propertyKey="@Properties.display_arbor" title="@Strings.display_arbor_title">
-<settingConfig type="boolean" />
-</setting>
-</settings> \ No newline at end of file
diff --git a/resources/strings.xml b/resources/strings.xml
index e16a8a9..052b7e2 100644
--- a/resources/strings.xml
+++ b/resources/strings.xml
@@ -1,8 +1,3 @@
<strings>
- <string id="AppName">Copernicus</string>
-
-
- <string id="display_dark_title">Dark mode?</string>
- <string id="display_seconds_title">Display second hand?</string>
- <string id="display_arbor_title">Display arbor?</string>
+ <string id="AppName">Mazarine Watch</string>
</strings>
diff --git a/source/CopernicusApp.mc b/source/CopernicusApp.mc
deleted file mode 100644
index 982f397..0000000
--- a/source/CopernicusApp.mc
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copernicus
-//
-// Copyright (C) 2019 cy384
-// All rights reserved.
-//
-// This software may be modified and distributed under the terms
-// of the BSD license. See the LICENSE file for details.
-//
-
-using Toybox.Application;
-using Toybox.WatchUi;
-
-class CopernicusApp extends Application.AppBase
-{
- function initialize()
- {
- AppBase.initialize();
- }
-
- function onStart(state)
- {
- }
-
- function onStop(state)
- {
- }
-
- function getInitialView()
- {
- return [new CopernicusView()];
- }
-
- function onSettingsChanged()
- {
- WatchUi.requestUpdate();
- }
-}
diff --git a/source/CopernicusView.mc b/source/CopernicusView.mc
deleted file mode 100644
index bbb8b5e..0000000
--- a/source/CopernicusView.mc
+++ /dev/null
@@ -1,397 +0,0 @@
-//
-// Copernicus
-//
-// Copyright (C) 2019 cy384
-// All rights reserved.
-//
-// This software may be modified and distributed under the terms
-// of the BSD license. See the LICENSE file for details.
-//
-
-using Toybox.WatchUi;
-using Toybox.Graphics;
-using Toybox.System;
-using Toybox.Math;
-using Toybox.Application;
-
-class CopernicusView extends WatchUi.WatchFace
-{
- var awake;
- var arbor;
- var seconds;
- var dark;
-
- function initialize()
- {
- WatchUi.WatchFace.initialize();
-
- awake = false;
- arbor = true;
- seconds = true;
- dark = true;
- }
-
- function onLayout(dc)
- {
- }
-
- // "Called when this View is brought to the foreground. Restore
- // the state of this View and prepare it to be shown. This includes
- // loading resources into memory."
- function onShow() {
- }
-
- function drawMinutesMarks(dc)
- {
- var width = dc.getWidth();
- var height = dc.getHeight();
-
- var min;
- if (width > height)
- {
- min = height;
- }
- else
- {
- min = width;
- }
-
- var iX, iY, oX, oY;
- var furtherOuterRad = min * 0.37;
- var outerRad = min * 0.35;
- var innerRad = min * 0.33;
-
- // circles
- dc.setPenWidth(1);
- dc.drawCircle(width/2.0, height/2.0, outerRad);
- dc.drawCircle(width/2.0, height/2.0, innerRad);
-
- if (dark)
- {
- dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
- }
- else
- {
- dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
- }
-
- // minute marks
- dc.setPenWidth(1);
- for (var x = 0; x <= 60; x += 1) {
- var angle = x * (Math.PI / 30.0);
-
- // make the 5 minute marks longer
- if (x % 5 == 0)
- {
- oX = width/2.0 + Math.sin(angle)*furtherOuterRad;
- oY = height/2.0 + Math.cos(angle)*furtherOuterRad;
- }
- else
- {
- oX = width/2.0 + Math.sin(angle)*outerRad;
- oY = height/2.0 + Math.cos(angle)*outerRad;
- }
-
- iX = width/2.0 + Math.sin(angle)*innerRad;
- iY = height/2.0 + Math.cos(angle)*innerRad;
-
- dc.drawLine(iX, iY, oX, oY);
- }
- }
-
- function drawMinuteHand(dc)
- {
- var width = dc.getWidth();
- var height = dc.getHeight();
-
- var min;
- if (width > height)
- {
- min = height;
- }
- else
- {
- min = width;
- }
-
- var clockTime = System.getClockTime();
-
- var hourRad = min * 0.18;
- var pointerOut = min * 0.42;
- var pointerIn = min * 0.36;
-
- // get the angle for minutes
- var angle = (clockTime.min / 60.0) * (-2.0 * Math.PI);
- angle += Math.PI;
-
- // center of the hour circle
- var cX = width/2.0 + Math.sin(angle)*hourRad;
- var cY = height/2.0 + Math.cos(angle)*hourRad;
-
- // points for the hour nub
- var pox = width/2.0 + Math.sin(angle)*pointerOut;
- var poy = height/2.0 + Math.cos(angle)*pointerOut;
-
- var pix = width/2.0 + Math.sin(angle)*pointerIn;
- var piy = height/2.0 + Math.cos(angle)*pointerIn;
-
- // draw the hand
- dc.setPenWidth(4);
- dc.drawLine(pix, piy, pox, poy);
- dc.drawCircle(cX, cY, min/4.8);
-
- if (arbor)
- {
- // draw the arbor
- dc.fillCircle(width/2.0, height/2.0, min*0.03);
- }
- }
-
- function drawHourHand(dc)
- {
- var width = dc.getWidth();
- var height = dc.getHeight();
-
- var min;
- if (width > height)
- {
- min = height;
- }
- else
- {
- min = width;
- }
-
- var clockTime = System.getClockTime();
-
- var hourRad = min * 0.13;
- var pointerOut = min * 0.34;
- var pointerIn = min * 0.30;
-
- // get the angle for the hour
- var angle = ((clockTime.hour % 12) / 12.0) * (-2.0 * Math.PI);
- angle += Math.PI;
-
- // add the minutes
- angle -= (clockTime.min / 60.0) * (Math.PI / 6);
-
- // center of the circle
- var cX = width/2.0 + Math.sin(angle)*hourRad;
- var cY = height/2.0 + Math.cos(angle)*hourRad;
-
- // points for the nub
- var pox = width/2.0 + Math.sin(angle)*pointerOut;
- var poy = height/2.0 + Math.cos(angle)*pointerOut;
-
- var pix = width/2.0 + Math.sin(angle)*pointerIn;
- var piy = height/2.0 + Math.cos(angle)*pointerIn;
-
- dc.setPenWidth(5);
- dc.drawLine(pix, piy, pox, poy);
- dc.fillCircle(cX, cY, min/5.3);
- }
-
- function drawSecondHand(dc)
- {
- var width = dc.getWidth();
- var height = dc.getHeight();
-
- var min;
- if (width > height)
- {
- min = height;
- }
- else
- {
- min = width;
- }
-
- var clockTime = System.getClockTime();
-
- var secondRad = min * 0.45;
- var secondRadBack = min * 0.2;
-
- // get the angle for seconds
- var angle = (clockTime.sec / 60.0) * (-2.0 * Math.PI);
- angle += Math.PI;
-
- // tip of the second hand
- var slx = width/2.0 + Math.sin(angle)*secondRad;
- var sly = height/2.0 + Math.cos(angle)*secondRad;
-
- // draw second hand
- dc.setPenWidth(2);
- dc.drawLine(width/2.0, height/2.0, slx, sly);
-
- // back side of second hand
- var back = angle + Math.PI;
- var bx = width/2.0 + Math.sin(back)*secondRadBack;
- var by = height/2.0 + Math.cos(back)*secondRadBack;
-
- dc.setPenWidth(2);
- dc.drawLine(width/2.0, height/2.0, bx, by);
-
- // make a triangle shape on the back
- var left = back - 0.02*Math.PI;
- var right = back + 0.02*Math.PI;
-
- dc.fillPolygon([[width/2.0, height/2.0],
- [width/2.0 + Math.sin(left)*secondRadBack,
- height/2.0 + Math.cos(left)*secondRadBack],
- [width/2.0 + Math.sin(right)*secondRadBack,
- height/2.0 + Math.cos(right)*secondRadBack]]);
-
- if (arbor)
- {
- // draw the arbor
- dc.fillCircle(width/2.0, height/2.0, min*0.02);
- }
- }
-
- function drawDots(dc)
- {
- var width = dc.getWidth();
- var height = dc.getHeight();
-
- var min;
- if (width > height)
- {
- min = height;
- }
- else
- {
- min = width;
- }
-
- // top dots
- if (dark)
- {
- // dark variant has two dots on top
- dc.fillCircle(width * 0.46, height * 0.04, min/33);
- dc.fillCircle(width * 0.54, height * 0.04, min/33);
- }
- else
- {
- dc.fillCircle(width * 0.50, height * 0.04, min/33);
- }
-
- // other cardinal dots
- dc.fillCircle(width * 0.50, height * 0.96, min/33);
- dc.fillCircle(width * 0.96, height * 0.50, min/33);
- dc.fillCircle(width * 0.04, height * 0.50, min/33);
-
- if (!dark)
- {
- // light variant has more dots at 45* angles
- // aka sqrt(3)/2
- var ord = 0.8660254037844386;
-
- // hour dots
- dc.fillCircle(width*0.5*(1 + 0.92*ord), height*0.5*(1 + 0.92*0.5), min/40);
- dc.fillCircle(width*0.5*(1 + 0.92*ord), height*0.5*(1 - 0.92*0.5), min/40);
- dc.fillCircle(width*0.5*(1 - 0.92*ord), height*0.5*(1 + 0.92*0.5), min/40);
- dc.fillCircle(width*0.5*(1 - 0.92*ord), height*0.5*(1 - 0.92*0.5), min/40);
-
- dc.fillCircle(width*0.5*(1 + 0.92*0.5), height*0.5*(1 + 0.92*ord), min/40);
- dc.fillCircle(width*0.5*(1 + 0.92*0.5), height*0.5*(1 - 0.92*ord), min/40);
- dc.fillCircle(width*0.5*(1 - 0.92*0.5), height*0.5*(1 + 0.92*ord), min/40);
- dc.fillCircle(width*0.5*(1 - 0.92*0.5), height*0.5*(1 - 0.92*ord), min/40);
- }
- }
-
- // update event handler
- function onUpdate(dc)
- {
- dark = Application.getApp().getProperty("display_dark");
- arbor = Application.getApp().getProperty("display_arbor");
- seconds = Application.getApp().getProperty("display_seconds");
-
- var width = dc.getWidth();
- var height = dc.getHeight();
-
- var min;
- if (width > height)
- {
- min = height;
- }
- else
- {
- min = width;
- }
-
- // bg
- if (dark)
- {
- dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
- }
- else
- {
- dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
- }
- dc.fillRectangle(0, 0, width, height);
-
- // draw all dots
- dc.setColor(Graphics.COLOR_YELLOW, Graphics.COLOR_TRANSPARENT);
- drawDots(dc);
-
- // minute rings with marks
- if (dark)
- {
- dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
- }
- else
- {
- dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
- }
- drawMinutesMarks(dc);
-
- // draw hour hand
- dc.setColor(Graphics.COLOR_YELLOW, Graphics.COLOR_TRANSPARENT);
- drawHourHand(dc);
-
- // draw minute hand
- if (dark)
- {
- dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
- }
- else
- {
- dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
- }
- drawMinuteHand(dc);
-
- // if we're awake, draw the second hand
- if (awake && seconds)
- {
- if (dark)
- {
- dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
- }
- else
- {
- dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
- }
-
- drawSecondHand(dc);
- }
- }
-
- // "Called when this View is removed from the screen. Save the
- // state of this View here. This includes freeing resources from
- // memory."
- function onHide() {
- }
-
- // called when we enter sleep
- function onEnterSleep()
- {
- awake = false;
- WatchUi.requestUpdate();
- }
-
- // called when we exit sleep
- function onExitSleep()
- {
- awake = true;
- }
-}
diff --git a/source/MazarineApp.mc b/source/MazarineApp.mc
new file mode 100644
index 0000000..b334540
--- /dev/null
+++ b/source/MazarineApp.mc
@@ -0,0 +1,28 @@
+// Mazarine
+//
+// Copyright (C) 2019 cy384, 2020 cos
+// All rights reserved.
+//
+// This software may be modified and distributed under the terms
+// of the BSD license. See the LICENSE file for details.
+
+using Toybox.Application;
+using Toybox.WatchUi;
+
+class MazarineApp extends Application.AppBase {
+ function initialize() {
+ AppBase.initialize();
+ }
+
+ function onStart(_) { }
+
+ function onStop(_) { }
+
+ function getInitialView() {
+ return [new MazarineView()];
+ }
+
+ function onSettingsChanged() {
+ WatchUi.requestUpdate();
+ }
+}
diff --git a/source/MazarineView.mc b/source/MazarineView.mc
new file mode 100644
index 0000000..3a684c2
--- /dev/null
+++ b/source/MazarineView.mc
@@ -0,0 +1,392 @@
+// Mazarine
+//
+// Copyright (C) 2019 cy384, 2020 cos
+// All rights reserved.
+//
+// This software may be modified and distributed under the terms
+// of the BSD license. See the LICENSE file for details.
+
+using Toybox.WatchUi;
+using Toybox.Graphics;
+using Toybox.System;
+using Toybox.Math;
+using Toybox.Application;
+using Toybox.Time.Gregorian;
+
+class MazarineView extends WatchUi.WatchFace {
+
+ function initialize() {
+ WatchUi.WatchFace.initialize();
+ }
+
+ function onLayout(_) { }
+
+ function onShow() { }
+
+ function draw_root_window(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+
+ var min;
+ if (width > height) {
+ min = height;
+ } else {
+ min = width;
+ }
+
+ var iX, iY, oX, oY;
+ var furtherOuterRad = min * 0.46;
+ var outerRad = min * 0.48;
+ var innerRad = min * 0.52;
+
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+
+ // minute marks
+ dc.setPenWidth(2);
+ for (var x = 0; x <= 60; x += 1) {
+ var angle = x * (Math.PI / 30.0);
+
+ // make the 5 minute marks longer
+ if (x % 5 == 0) {
+ oX = width/2.0 + Math.sin(angle)*furtherOuterRad;
+ oY = height/2.0 + Math.cos(angle)*furtherOuterRad;
+ } else {
+ oX = width/2.0 + Math.sin(angle)*outerRad;
+ oY = height/2.0 + Math.cos(angle)*outerRad;
+ }
+
+ iX = width/2.0 + Math.sin(angle)*innerRad;
+ iY = height/2.0 + Math.cos(angle)*innerRad;
+
+ dc.drawLine(iX, iY, oX, oY);
+ }
+ }
+
+ function draw_battery_meter(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+
+ var stats = System.getSystemStats();
+ var battery_meter_width = 30;
+ var battery_meter_height = 5;
+ // var battery_meter_x = 1.0 * width/5.0 - battery_meter_width/2.0;
+ // var battery_meter_y = height/2.0 - battery_meter_height/2.0;
+ var battery_meter_x = width/2.0 - battery_meter_width/2.0;
+ var battery_meter_y = 7.0 * height/8.0 - battery_meter_height/2.0;
+ dc.setPenWidth(1);
+ dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
+ dc.drawRectangle(battery_meter_x - 1, battery_meter_y - 1,
+ battery_meter_width + 2, battery_meter_height + 2);
+ if (stats.battery < 25.0) {
+ dc.setColor(Graphics.COLOR_RED, Graphics.COLOR_BLACK);
+ } else {
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ }
+ dc.fillRectangle(battery_meter_x, battery_meter_y,
+ battery_meter_width * stats.battery/100.0, battery_meter_height);
+ }
+
+ function draw_timezone(dc, index, name, offset) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+ var font = Graphics.FONT_XTINY;
+ var x_pos = 1.0 * width/4.0;
+ // var x_pos = 1.0 * width/2.0;
+ // var y_pos = height/2.0-dc.getFontHeight(font)/2.0;
+ var y_pos = height/2.0-dc.getFontHeight(font)*2.5+index *
+ dc.getFontHeight(font);
+ // var y_pos = height/3.0-dc.getFontHeight(font)/2.0;
+ // now ⇒ Toybox::Time::Moment
+ var now = Time.now();
+ var dur = new Time.Duration(offset * Gregorian.SECONDS_PER_HOUR);
+ // add(offset * duration) ⇒ Toybox::Time::Moment
+ var when = now.add(dur);
+ var time = Gregorian.utcInfo(when, Time.FORMAT_MEDIUM);
+ // utcInfo(moment, format) ⇒ Toybox::Time::Gregorian::Info
+ // var time = Time.Moment(Time.now(), Time.FORMAT_MEDIUM);
+
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ dc.drawText(x_pos, y_pos, font, name + " " + time.hour.format("%02d") +
+ ":" + time.min.format("%02d"), Graphics.TEXT_JUSTIFY_CENTER);
+ }
+
+ function draw_temperature(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+ var font = Graphics.FONT_XTINY;
+ var x_pos = 1.0 * width/4.0;
+ var y_pos = height/2.0-dc.getFontHeight(font)/2.0;
+
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ dc.drawText(x_pos, y_pos, font, Toybox.SensorHistory
+ .getTemperatureHistory({}).next().data
+ .format("%.1d") + "°C", Graphics.TEXT_JUSTIFY_CENTER);
+ }
+
+ function draw_timezones(dc) {
+ draw_utc_offset(dc);
+ draw_timezone(dc, 1, "HK", 8);
+ // draw_timezone(dc, 3, "ACDT", 10.5);
+ draw_timezone(dc, 2, "NSW", 11);
+ draw_timezone(dc, 3, "NY", -4);
+ draw_timezone(dc, 4, "CA", -7);
+ }
+
+ function draw_utc_offset(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+ var font = Graphics.FONT_XTINY;
+ var x_pos = 1.0 * width/2.0;
+ var y_pos = 1.0 * height/4.0-dc.getFontHeight(font)/2.0;
+ var myTime = System.getClockTime();
+ var tz_string = "UTC ";
+
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ if ( myTime.timeZoneOffset/3600 >= 0 ) {
+ tz_string += "+";
+ }
+ tz_string += myTime.timeZoneOffset/3600;
+ if ( myTime.timeZoneOffset % 3600 ) {
+ tz_string += ":" + myTime.timeZoneOffset / 60 % 60;
+ }
+ dc.drawText(x_pos, y_pos, font, tz_string,
+ Graphics.TEXT_JUSTIFY_CENTER);
+ }
+
+ function draw_dayday(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+ var today = Gregorian.info(Time.now(), Time.FORMAT_MEDIUM);
+ var clockTime = System.getClockTime();
+ var minute_hand_obscures = false;
+ var hour_hand_obscures = false;
+ var dayday_font = Graphics.FONT_XTINY;
+ var x_pos = 3.0 * width/4.0;
+ var y_pos = height/2.0-dc.getFontHeight(dayday_font)/2.0;
+
+ if (clockTime.hour % 12 == 2 and clockTime.min >= 35) {
+ hour_hand_obscures = true;
+ }
+
+ if (clockTime.hour % 12 == 3 and clockTime.min <= 35) {
+ hour_hand_obscures = true;
+ }
+
+ if (clockTime.min >= 14 and clockTime.min <= 17) {
+ minute_hand_obscures = true;
+ }
+
+ if ( minute_hand_obscures ) {
+ if ( clockTime.hour % 12 == 2 ) {
+ y_pos += 1.5 * dc.getFontHeight(dayday_font);
+ } else {
+ y_pos -= 1.5 * dc.getFontHeight(dayday_font);
+ }
+ } else if ( hour_hand_obscures ) {
+ if ( clockTime.min > 15 && clockTime.min < 30 ) {
+ y_pos -= 1.5 * dc.getFontHeight(dayday_font);
+ } else {
+ y_pos += 1.5 * dc.getFontHeight(dayday_font);
+ }
+ }
+
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ dc.drawText(x_pos, y_pos, dayday_font, today.day_of_week + " " +
+ today.day, Graphics.TEXT_JUSTIFY_CENTER);
+ }
+
+ function draw_digital_time(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+ var time_font = Graphics.FONT_LARGE;
+ var x_pos = width/2.0;
+ var y_pos = 4*height/5.0-dc.getFontHeight(time_font)/2.0;
+ var clockTime = System.getClockTime();
+
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ dc.drawText(x_pos, y_pos, time_font, clockTime.hour.format("%02d") +
+ ":" + clockTime.min.format("%02d"), Graphics.TEXT_JUSTIFY_CENTER);
+ }
+
+ function draw_minute_hand(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+
+ var min;
+ if (width > height) {
+ min = height;
+ } else {
+ min = width;
+ }
+
+ var clockTime = System.getClockTime();
+
+ var hourRad = min * 0.18;
+ var pointerOut = min * 0.45;
+ var pointerIn = min * 0.00;
+
+ // get the angle for minutes
+ var angle = (clockTime.min / 60.0) * (-2.0 * Math.PI);
+ angle += Math.PI;
+
+ // points for the hour nub
+ var pox = width/2.0 + Math.sin(angle)*pointerOut;
+ var poy = height/2.0 + Math.cos(angle)*pointerOut;
+
+ var pix = width/2.0 + Math.sin(angle)*pointerIn;
+ var piy = height/2.0 + Math.cos(angle)*pointerIn;
+
+ // draw the hand
+ dc.setPenWidth(4);
+ dc.drawLine(pix, piy, pox, poy);
+
+ // draw the arbor
+ dc.fillCircle(width/2.0, height/2.0, min*0.03);
+ }
+
+ function draw_hour_hand(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+
+ var min;
+ if (width > height) {
+ min = height;
+ } else {
+ min = width;
+ }
+
+ var clockTime = System.getClockTime();
+
+ var hourRad = min * 0.13;
+ var pointerOut = min * 0.34;
+ var pointerIn = min * 0.00;
+
+ // get the angle for the hour
+ var angle = ((clockTime.hour % 12) / 12.0) * (-2.0 * Math.PI);
+ angle += Math.PI;
+
+ // add the minutes
+ angle -= (clockTime.min / 60.0) * (Math.PI / 6);
+
+ // center of the circle
+ var cX = width/2.0 + Math.sin(angle)*hourRad;
+ var cY = height/2.0 + Math.cos(angle)*hourRad;
+
+ // points for the nub
+ var pox = width/2.0 + Math.sin(angle)*pointerOut;
+ var poy = height/2.0 + Math.cos(angle)*pointerOut;
+
+ var pix = width/2.0 + Math.sin(angle)*pointerIn;
+ var piy = height/2.0 + Math.cos(angle)*pointerIn;
+
+ dc.setPenWidth(5);
+ dc.drawLine(pix, piy, pox, poy);
+ }
+
+ function draw_second_hand(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+ var iX, iY, oX, oY;
+
+ var min;
+ if (width > height) {
+ min = height;
+ } else {
+ min = width;
+ }
+
+ var clockTime = System.getClockTime();
+
+ // var secondRad = min * 0.52;
+ // var secondRadBack = min * 0.0;
+ var furtherOuterRad = min * 0.46;
+ var outerRad = min * 0.48;
+ var innerRad = min * 0.52;
+
+ // get the angle for seconds
+ var angle = (clockTime.sec / 60.0) * (-2.0 * Math.PI);
+
+ for (var x = clockTime.sec - 1; x <= clockTime.sec + 1; x += 1) {
+ var angle = x * (Math.PI / 30.0);
+ dc.setPenWidth(4);
+ oX = width/2.0 + Math.sin(angle)*furtherOuterRad;
+ oY = height/2.0 - Math.cos(angle)*furtherOuterRad;
+ iX = width/2.0 + Math.sin(angle)*innerRad;
+ iY = height/2.0 - Math.cos(angle)*innerRad;
+ dc.setClip(oX-10, oY-10, 20, 20);
+
+ if ( x == clockTime.sec) {
+ dc.setColor(Graphics.COLOR_RED, Graphics.COLOR_TRANSPARENT);
+ } else {
+ dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
+ dc.drawLine(iX, iY, oX, oY);
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ dc.setPenWidth(2);
+
+ // make the 5 minute marks longer
+ if (x % 5 == 0) {
+ oX = width/2.0 + Math.sin(angle)*furtherOuterRad;
+ oY = height/2.0 - Math.cos(angle)*furtherOuterRad;
+ } else {
+ oX = width/2.0 + Math.sin(angle)*outerRad;
+ oY = height/2.0 - Math.cos(angle)*outerRad;
+ }
+ }
+
+ dc.drawLine(iX, iY, oX, oY);
+ }
+ }
+
+ // update event handler
+ function onUpdate(dc) {
+ var width = dc.getWidth();
+ var height = dc.getHeight();
+ // dc.setClip(0, 0, width, height);
+ dc.clearClip();
+
+ var min;
+ if (width > height) {
+ min = height;
+ } else {
+ min = width;
+ }
+
+ // bg
+ dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
+ dc.fillRectangle(0, 0, width, height);
+
+ // minute rings with marks
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+ draw_root_window(dc);
+ draw_digital_time(dc);
+ draw_dayday(dc);
+ draw_timezones(dc);
+
+ draw_battery_meter(dc);
+ // draw hour hand
+ dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
+ draw_hour_hand(dc);
+
+ // draw minute hand
+ dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
+ draw_minute_hand(dc);
+
+ dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT);
+
+ dc.setColor(Graphics.COLOR_RED, Graphics.COLOR_TRANSPARENT);
+ draw_second_hand(dc);
+ }
+
+ function onPartialUpdate(dc) {
+ dc.setColor(Graphics.COLOR_RED, Graphics.COLOR_TRANSPARENT);
+ draw_second_hand(dc);
+ }
+
+ function onHide() { }
+
+ function onEnterSleep() {
+ WatchUi.requestUpdate();
+ }
+
+ function onExitSleep() { }
+}