Philip Abbey 75ea23dfbd 7 New devices, mainly Fenix
SDK updated to 7.3.0 in order to get new devices to work in simulation. This means some function prototypes have had to be amended.
2024-09-05 21:23:49 +01:00

153 lines
5.4 KiB
MonkeyC

//-----------------------------------------------------------------------------------
//
// Distributed under MIT Licence
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
//
//-----------------------------------------------------------------------------------
//
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
// tested on a Venu 2 device. The source code is provided at:
// https://github.com/house-of-abbey/GarminHomeAssistant.
//
// J D Abbey & P A Abbey, 28 December 2022
//
//
// Description:
//
// ErrorView provides a means to present application errors to the user. These
// should not happen of course... but they do, so best make sure errors can be
// reported.
//
// Designed so that a single ErrorView is used for all errors and hence can ensure
// that only the first call to display is honoured until the view is dismissed.
// This compensates for older devices not being able to call WatchUi.getCurrentView()
// due to not supporting API level 3.4.0.
//
// Usage:
// 1) ErrorView.show("Error message");
// 2) return ErrorView.create("Error message"); // as Lang.Array<WatchUi.Views or WatchUi.InputDelegates>
//
//-----------------------------------------------------------------------------------
using Toybox.Graphics;
using Toybox.Lang;
using Toybox.WatchUi;
using Toybox.Communications;
using Toybox.Timer;
class ErrorView extends ScalableView {
private static const scErrorIconMargin as Lang.Float = 7f;
private var mText as Lang.String = "";
private var mDelegate as ErrorDelegate;
// Vertical spacing between the top of the face and the error icon
private var mErrorIconMargin as Lang.Number;
private var mErrorIcon;
private var mTextArea as WatchUi.TextArea or Null;
private var mAntiAlias as Lang.Boolean = false;
private static var instance;
private static var mShown as Lang.Boolean = false;
function initialize() {
ScalableView.initialize();
mDelegate = new ErrorDelegate(self);
// Convert the settings from % of screen size to pixels
mErrorIconMargin = pixelsForScreen(scErrorIconMargin);
mErrorIcon = Application.loadResource(Rez.Drawables.ErrorIcon) as Graphics.BitmapResource;
if (Graphics.Dc has :setAntiAlias) {
mAntiAlias = true;
}
}
// Load your resources here
function onLayout(dc as Graphics.Dc) as Void {
var w = dc.getWidth();
mTextArea = new WatchUi.TextArea({
:text => mText,
:color => Graphics.COLOR_WHITE,
:font => Graphics.FONT_XTINY,
:justification => Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER,
:locX => 0,
:locY => pixelsForScreen(20.0),
:width => w,
:height => pixelsForScreen(60.0)
});
}
// Update the view
function onUpdate(dc as Graphics.Dc) as Void {
var w = dc.getWidth();
if (mAntiAlias) {
dc.setAntiAlias(true);
}
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLUE);
dc.clear();
dc.drawBitmap(w/2 - mErrorIcon.getWidth()/2, mErrorIconMargin, mErrorIcon);
mTextArea.draw(dc);
}
function getDelegate() as ErrorDelegate {
return mDelegate;
}
static function create(text as Lang.String) as [ WatchUi.Views ] or [ WatchUi.Views, WatchUi.InputDelegates ] {
if (instance == null) {
instance = new ErrorView();
}
if (!mShown) {
instance.setText(text);
mShown = true;
}
return [instance, instance.getDelegate()];
}
// Create or reuse an existing ErrorView, and pass on the text.
static function show(text as Lang.String) as Void {
if (!mShown) {
create(text); // Ignore returned values
WatchUi.pushView(instance, instance.getDelegate(), WatchUi.SLIDE_UP);
// This must be last to avoid a race condition with unShow(), where the
// ErrorView can't be dismissed.
mShown = true;
}
}
static function unShow() as Void {
if (mShown) {
WatchUi.popView(WatchUi.SLIDE_DOWN);
// The call to 'updateMenuItems()' must be on another thread so that the view is popped above.
var myTimer = new Timer.Timer();
// Now this feels very "closely coupled" to the application, but it is the most reliable method instead of using a timer.
myTimer.start(getApp().method(:updateMenuItems), Globals.scApiResume, false);
// This must be last to avoid a race condition with show(), where the
// ErrorView can't be dismissed.
mShown = false;
}
}
// Internal show now we're not a static method like 'show()'.
function setText(text as Lang.String) as Void {
mText = text;
if (mTextArea != null) {
mTextArea.setText(text);
requestUpdate();
}
}
}
class ErrorDelegate extends WatchUi.BehaviorDelegate {
function initialize(view as ErrorView) {
WatchUi.BehaviorDelegate.initialize();
}
function onBack() as Lang.Boolean {
getApp().getQuitTimer().reset();
ErrorView.unShow();
return true;
}
}