A simple analog watch face for the Garmin Forerunner 55.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

489 lines
16 KiB

using Toybox.Graphics as Gfx;
using Toybox.System as Sys;
using Toybox.Lang as Lang;
using Toybox.Math as Math;
using Toybox.Time as Time;
using Toybox.Time.Gregorian as Calendar;
using Toybox.WatchUi as Ui;
using Toybox.Application as App;
using Toybox.ActivityMonitor as ActMon;
using Toybox.SensorHistory as Sensor;
using Toybox.Activity as Activity;
using Toybox.UserProfile as User;
enum
{
LAT,
LON
}
class SnapshotWatchView extends Ui.WatchFace {
var usePreferences = true;
var showHeartRate = true;
var showSteps = true;
var showRiseSet = false;
var showSecondTime = false;
var secondTimeOffset = 0;
var graphLength = 240;
var arrayColours = new [11];
var graphColour = 0;
var useZonesColour = true;
var heartRateZones;
var showMoveBar = false;
var background_color = Gfx.COLOR_BLACK;
// var background_color = Gfx.COLOR_BLUE;
var mainTextColor = Gfx.COLOR_WHITE;
var showSeconds = false;
var width_screen, height_screen;
var hashMarksArray = new [60];
var heartNow;
var heartMin = 1000;
var heartMax = 0;
// var proFeatures = true;
public function initialize() {
Ui.WatchFace.initialize();
}
//! Load your resources here
function onLayout(dc) {
//get screen dimensions
width_screen = dc.getWidth();
height_screen = dc.getHeight();
//colours and zones
arrayColours = [Gfx.COLOR_DK_GRAY, Gfx.COLOR_RED, Gfx.COLOR_DK_RED, Gfx.COLOR_ORANGE, Gfx.COLOR_YELLOW, Gfx.COLOR_GREEN, Gfx.COLOR_DK_GREEN, Gfx.COLOR_BLUE, Gfx.COLOR_DK_BLUE, Gfx.COLOR_PURPLE, Gfx.COLOR_PINK];
heartRateZones = User.getHeartRateZones(User.getCurrentSport());
// heartRateZones = [98, 127, 146, 166, 185, 195];
//get hash marks position
for(var i = 0; i < 60; i+=1)
{
hashMarksArray[i] = new [2];
hashMarksArray[i][0] = (i / 60.0) * Math.PI * 2;
if(i != 0 && i != 15 && i != 30 && i != 45)
{ hashMarksArray[i][1] = -95; }
else
{ hashMarksArray[i][1] = -85; }
}
setLayout(Rez.Layouts.WatchFace(dc));
}
//! Restore the state of the app and prepare the view to be shown
function onShow() {
}
//! Update the view
function onUpdate(dc) {
//var bgImage = Ui.loadResource(Rez.Drawables.BackgroundImage);
var code = "0";
if (usePreferences)
{
code = Application.getApp().getProperty("code");
showHeartRate = Application.getApp().getProperty("showHeartRate");
graphLength = Application.getApp().getProperty("graphLength");
graphColour = Application.getApp().getProperty("graphColour");
useZonesColour = Application.getApp().getProperty("useZonesColour");
showMoveBar = Application.getApp().getProperty("showMoveBar");
showSteps = Application.getApp().getProperty("showSteps");
showRiseSet = Application.getApp().getProperty("showRiseSet");
showSecondTime = Application.getApp().getProperty("showSecondTime");
secondTimeOffset = Application.getApp().getProperty("secondTimeOffset");
}
// authenticate(code);
// if (!proFeatures)
// {
// showSteps = true;
// showRiseSet = false;
// showSecondTime = false;
// }
// if (showSecondTime)
// {
// if (secondTimeOffset <= 24 && secondTimeOffset >= -24)
// {
// secondTimeOffset = secondTimeOffset.toNumber();
// }
// else
// {
// showSecondTime = false;
// secondTimeOffset = 0;
// }
// }
// Clear screen
dc.setColor(background_color, Gfx.COLOR_WHITE);
dc.fillRectangle(0,0, width_screen, height_screen);
//dc.drawBitmap(0, 0, Ui.loadResource(Rez.Drawables.BackgroundImage));
//var myActivity = Activity.getActivityInfo();
heartNow = Activity.getActivityInfo().currentHeartRate;
// Plot heart rate graph
//plotHRgraph(dc);
// Draw hash marks
drawHashMarks(dc);
// Set text colour for the remaining information
dc.setColor(mainTextColor, Gfx.COLOR_TRANSPARENT);
// Show cur/min/max HR values (recorded when plotting graph, above)
if (showHeartRate)
{
if (heartNow == null)
{ dc.drawText(width_screen/2, height_screen/2 + 70, Gfx.FONT_SMALL, "-- bpm", Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER); }
else
{ dc.drawText(width_screen/2, height_screen/2 + 70, Gfx.FONT_SMALL, Lang.format("$1$ bpm", [heartNow]), Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER); }
}
// Show date
dc.setColor(Gfx.COLOR_DK_BLUE, Gfx.COLOR_TRANSPARENT);
dc.drawCircle(width_screen/2-50, height_screen/2, 32);
dc.setColor(mainTextColor, Gfx.COLOR_TRANSPARENT);
drawDate(dc);
// Show Status Icons
var offset = 8;
var heightOffset = 75;
var spreadOffset = offset*2;
dc.setColor(Gfx.COLOR_DK_BLUE, Gfx.COLOR_TRANSPARENT);
dc.drawEllipse(width_screen/2, height_screen/2-heightOffset+6, 37, 12);
dc.setColor(mainTextColor, Gfx.COLOR_TRANSPARENT);
// Show bluetooth icon
if (Sys.getDeviceSettings().phoneConnected)
{ dc.drawBitmap(width_screen/2-offset-4, height_screen/2-heightOffset, Ui.loadResource(Rez.Drawables.BluetoothCon)); }
else
{ dc.drawBitmap(width_screen/2-offset-4, height_screen/2-heightOffset, Ui.loadResource(Rez.Drawables.BluetoothDC)); }
// Show alarm icon
if (Sys.getDeviceSettings().alarmCount > 0)
{ dc.drawBitmap(width_screen/2-offset-spreadOffset-4, height_screen/2-heightOffset, Ui.loadResource(Rez.Drawables.AlarmIconSet)); }
else
{ dc.drawBitmap(width_screen/2-offset-spreadOffset-4, height_screen/2-heightOffset, Ui.loadResource(Rez.Drawables.AlarmIconClr)); }
// Show do not disturb icon
if (Sys.getDeviceSettings().doNotDisturb)
{ dc.drawBitmap(width_screen/2+offset+spreadOffset-4, height_screen/2-heightOffset, Ui.loadResource(Rez.Drawables.MuteIcon)); }
else
{ dc.drawBitmap(width_screen/2+offset+spreadOffset-4, height_screen/2-heightOffset, Ui.loadResource(Rez.Drawables.VolumeIcon)); }
// Show notification count icon
var numNotifications = Sys.getDeviceSettings().notificationCount;
if (numNotifications >= 10) { spreadOffset += 2; }
dc.drawBitmap(width_screen/2+offset-4, height_screen/2-heightOffset, Ui.loadResource(Rez.Drawables.NotificationIcon));
if (numNotifications > 0) {
dc.setColor(Gfx.COLOR_RED, Gfx.COLOR_TRANSPARENT);
dc.drawText(width_screen/2+offset+10-4, height_screen/2-heightOffset+5, Gfx.FONT_XTINY, numNotifications, Graphics.TEXT_JUSTIFY_RIGHT|Graphics.TEXT_JUSTIFY_VCENTER);
dc.setColor(mainTextColor, Gfx.COLOR_TRANSPARENT);
}
// Show Battery
var battery = Sys.getSystemStats().battery;
offset = 40;
heightOffset = 7;
dc.setColor(Gfx.COLOR_DK_BLUE, Gfx.COLOR_TRANSPARENT);
dc.drawCircle(width_screen/2+offset+13, height_screen/2, 26);
dc.setColor(mainTextColor, Gfx.COLOR_TRANSPARENT);
// if (battery == 100) { offset -= 2; }
if (battery >= 90) {
// offset += 4;
dc.drawBitmap(width_screen/2+offset+1, height_screen/2+3-heightOffset, Ui.loadResource(Rez.Drawables.BatteryFullIcon));
}
else if (battery >= 80) {
dc.drawBitmap(width_screen/2+offset+1, height_screen/2+3-heightOffset, Ui.loadResource(Rez.Drawables.Battery80Icon));
}
else if (battery >= 60) {
dc.drawBitmap(width_screen/2+offset+1, height_screen/2+3-heightOffset, Ui.loadResource(Rez.Drawables.Battery60Icon));
}
else if (battery >= 40) {
dc.drawBitmap(width_screen/2+offset+1, height_screen/2+3-heightOffset, Ui.loadResource(Rez.Drawables.Battery40Icon));
}
else if (battery >= 20) {
dc.drawBitmap(width_screen/2+offset+1, height_screen/2+3-heightOffset, Ui.loadResource(Rez.Drawables.Battery20Icon));
}
else {
dc.drawBitmap(width_screen/2+offset+1, height_screen/2+3-heightOffset, Ui.loadResource(Rez.Drawables.Battery0Icon));
}
dc.drawText(width_screen/2+offset+15, height_screen/2-3-heightOffset, Gfx.FONT_XTINY, Lang.format("$1$%", [battery.format("%2d")]), Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER);
// Show steps
dc.drawBitmap(width_screen/2-37, height_screen/2 - 58, Ui.loadResource(Rez.Drawables.ShoeIcon));
dc.drawText(width_screen/2, height_screen/2 - 45, Gfx.FONT_SMALL, ActMon.getInfo().steps, Graphics.TEXT_JUSTIFY_LEFT|Graphics.TEXT_JUSTIFY_VCENTER);
//dc.drawText(width_screen/2, height_screen/2 + 30, Gfx.FONT_SMALL, ActMon.getInfo().stepGoal, Graphics.TEXT_JUSTIFY_RIGHT|Graphics.TEXT_JUSTIFY_VCENTER);
if (showRiseSet)
{
// Show sunrise and sunset
drawSunriseSunset(dc);
}
// Show second (digital) time
drawDigitalTime(dc, Sys.getClockTime());
// Draw analogue clock hands
drawHands(dc, Sys.getClockTime().hour, Sys.getClockTime().min, Sys.getClockTime().sec);
}
//! The user has just looked at their watch. Timers and animations may be started here.
function onExitSleep() {
showSeconds = true;
}
//! Terminate any active timers and prepare for slow updates.
function onEnterSleep() {
showSeconds = false;
requestUpdate();
}
//! Draw the watch hand
function drawHand(dc, angle, whichHand, width, handColour)
{
dc.setColor(handColour, Gfx.COLOR_TRANSPARENT);
var length, r1, r2, r3, r4, deflect1, deflect2;
var centerX = width_screen/2;
var centerY = height_screen/2;
if (whichHand == 0) //hour hand
{
length = 0.6*centerX;
r1 = 0.0*length;
r2 = 0.39*length;
r3 = 0.49*length;
r4 = 1.1*length;
deflect1 = 0.10*width;
deflect2 = 0.08*width;
}
else //minute hand
{
length = 1.0*centerX;
r1 = 0.0*length;
r2 = 0.37*length;
r3 = 0.47*length;
r4 = 1.2*length;
deflect1 = 0.10*width;
deflect2 = 0.08*width;
}
var coords = [
[centerX + r1 * Math.sin(angle) , centerY - r1 * Math.cos(angle)],
[centerX + r2 * Math.sin(angle+deflect1) , centerY - r2 * Math.cos(angle+deflect1)],
[centerX + r3 * Math.sin(angle+deflect2) , centerY - r3 * Math.cos(angle+deflect2)],
[centerX + r4 * Math.sin(angle) , centerY - r4 * Math.cos(angle)],
[centerX + r3 * Math.sin(angle-deflect2) , centerY - r3 * Math.cos(angle-deflect2)],
[centerX + r2 * Math.sin(angle-deflect1) , centerY - r2 * Math.cos(angle-deflect1)],
[centerX + r1 * Math.sin(angle) , centerY - r1 * Math.cos(angle)]
];
dc.fillPolygon(coords);
}
function drawHands(dc, clock_hour, clock_min, clock_sec)
{
var hour, min, sec;
// Draw the hour hand - convert to minutes then compute angle
hour = ( ( ( clock_hour % 12 ) * 60 ) + clock_min ); // hour = 2*60.0;
hour = hour / (12 * 60.0) * Math.PI * 2;
drawHand(dc, hour, 0, 2.5, Gfx.COLOR_DK_BLUE);
drawHand(dc, hour, 0, 1.6, Gfx.COLOR_LT_GRAY);
// Draw the minute hand
min = ( clock_min / 60.0); // min = 40/60.0;
min = min * Math.PI * 2;
drawHand(dc, min, 1, 1.7, Gfx.COLOR_DK_BLUE);
drawHand(dc, min, 1, 1.0, Gfx.COLOR_LT_GRAY);
// Draw the seconds hand (use hash graphic here)
if(showSeconds)
{
sec = ( clock_sec / 60.0) * Math.PI * 2;
drawHash(dc, sec, width_screen/2, 4, 25, Gfx.COLOR_DK_BLUE);
drawHash(dc, sec, width_screen/2, 2, 25, Gfx.COLOR_LT_GRAY);
}
// Draw the inner circle
dc.setColor(Gfx.COLOR_DK_BLUE, background_color);
dc.fillCircle(width_screen/2, height_screen/2, 6);
dc.setColor(background_color,background_color);
dc.drawCircle(width_screen/2, height_screen/2, 6);
dc.fillCircle(width_screen/2, height_screen/2, 2);
}
function drawHash(dc, angle, length, width, overheadLine, handColour)
{
dc.setColor(handColour, Gfx.COLOR_TRANSPARENT);
var centerX = width_screen/2;
var centerY = height_screen/2;
var result = new [4];
var cos = Math.cos(angle);
var sin = Math.sin(angle);
var coords = [
[-(width/2), 0 + overheadLine],
[-(width/2), -length],
[width/2, -length],
[width/2, 0 + overheadLine]
];
for (var i = 0; i < 4; i += 1)
{
var x = (coords[i][0] * cos) - (coords[i][1] * sin);
var y = (coords[i][0] * sin) + (coords[i][1] * cos);
result[i] = [ centerX + x, centerY + y];
}
dc.fillPolygon(result);
}
//! Draw the hash mark symbols
function drawHashMarks(dc)
{
drawHash(dc, hashMarksArray[5][0], 110, 3, hashMarksArray[5][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[10][0], 110, 3, hashMarksArray[10][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[20][0], 110, 3, hashMarksArray[20][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[25][0], 110, 3, hashMarksArray[25][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[35][0], 110, 3, hashMarksArray[35][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[40][0], 110, 3, hashMarksArray[40][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[50][0], 110, 3, hashMarksArray[50][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[55][0], 110, 3, hashMarksArray[55][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[0][0], 110, 5, hashMarksArray[0][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[15][0], 110, 5, hashMarksArray[15][1], Gfx.COLOR_WHITE);
drawHash(dc, hashMarksArray[45][0], 110, 5, hashMarksArray[45][1], Gfx.COLOR_WHITE);
drawHash(dc, Math.PI, 110, 5, hashMarksArray[0][1], Gfx.COLOR_WHITE);
if(!showHeartRate)
{ drawHash(dc, hashMarksArray[30][0], 110, 5, hashMarksArray[30][1], Gfx.COLOR_WHITE); }
}
function drawDate(dc)
{
var info = Calendar.info(Time.now(), Time.FORMAT_LONG);
var dateStr = Lang.format("$1$ $2$", [info.month, info.day]);
dc.drawText(width_screen/2-50, height_screen/2 - 30, Gfx.FONT_TINY, info.day_of_week, Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(width_screen/2-50, height_screen/2 - 13, Gfx.FONT_TINY, dateStr, Gfx.TEXT_JUSTIFY_CENTER);
dc.drawText(width_screen/2-50, height_screen/2 + 6, Gfx.FONT_TINY, info.year-2000, Gfx.TEXT_JUSTIFY_CENTER);
}
function drawDigitalTime(dc, clockTime)
{
if (showSecondTime)
{
var offsetHour = clockTime.hour + secondTimeOffset;
if (offsetHour > 23)
{ offsetHour -= 24; }
else if (offsetHour < 0)
{ offsetHour += 24; }
var ampm = "AM";
if (offsetHour >= 12)
{ ampm = "PM"; }
var timeString = Lang.format("$1$:$2$ $3$", [to12hourFormat(offsetHour), clockTime.min.format("%02d"), ampm]);
dc.drawText(width_screen/2, height_screen/2 + 50, Gfx.FONT_SMALL, timeString, Gfx.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER);
}
}
function drawSunriseSunset(dc)
{
var sc = new SunCalc();
var lat;
var lon;
var timeStringRise = " --:--";
var timeStringSet = " --:--";
var loc = Activity.getActivityInfo().currentLocation;
if (loc == null)
{
lat = App.getApp().getProperty(LAT);
lon = App.getApp().getProperty(LON);
}
else
{
lat = loc.toDegrees()[0] * Math.PI / 180.0;
App.getApp().setProperty(LAT, lat);
lon = loc.toDegrees()[1] * Math.PI / 180.0;
App.getApp().setProperty(LON, lon);
}
// lat = -37.81400 * Math.PI / 180.0;
// lon = 144.96332 * Math.PI / 180.0;
if(lat != null && lon != null)
{
var ampm;
var now = new Time.Moment(Time.now().value());
var sunrise_moment = sc.calculate(now, lat.toDouble(), lon.toDouble(), SUNRISE);
var sunset_moment = sc.calculate(now, lat.toDouble(), lon.toDouble(), SUNSET);
var timeInfoSunrise = Calendar.info(sunrise_moment, Time.FORMAT_SHORT);
var timeInfoSunset = Calendar.info(sunset_moment, Time.FORMAT_SHORT);
ampm = "a";
if (timeInfoSunrise.hour >= 12)
{ ampm = "p"; }
timeStringRise = Lang.format("$1$:$2$$3$", [to12hourFormat(timeInfoSunrise.hour), timeInfoSunrise.min.format("%02d"), ampm]);
ampm = "a";
if (timeInfoSunset.hour >= 12)
{ ampm = "p"; }
timeStringSet = Lang.format("$1$:$2$$3$", [to12hourFormat(timeInfoSunset.hour), timeInfoSunset.min.format("%02d"), ampm]);
}
dc.drawText(3, height_screen/2 - 14, Gfx.FONT_SMALL, timeStringRise, Gfx.TEXT_JUSTIFY_LEFT|Graphics.TEXT_JUSTIFY_VCENTER);
dc.drawText(3, height_screen/2 + 11, Gfx.FONT_SMALL, timeStringSet, Gfx.TEXT_JUSTIFY_LEFT|Graphics.TEXT_JUSTIFY_VCENTER);
}
function to12hourFormat(hour)
{
var hour12 = hour % 12;
if (hour12 == 0)
{ hour12 = 12; }
return hour12;
}
// function authenticate(code) {
// proFeatures = true;
// // set proFeatures=false
// // insert your authentication requirements here
// // then change proFeatures=true if "code" variable is valid
// }
}