mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-04-30 20:52:27 +00:00
Glance now updates the status. Fix for quitting the application when persistently displaying an ErrorView. Added option for Widget RootView to immediately start HomeAssistant without waiting for a tap as requested by a user.
151 lines
5.2 KiB
MonkeyC
151 lines
5.2 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 var mText as Lang.String = "";
|
|
private var mDelegate as ErrorDelegate;
|
|
private const cSettings as Lang.Dictionary = {
|
|
:errorIconMargin => 7f
|
|
};
|
|
// Vertical spacing between the top of the face and the error icon
|
|
private var mErrorIconMargin as Lang.Number;
|
|
private var mErrorIcon;
|
|
private var mTextArea;
|
|
|
|
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(cSettings.get(:errorIconMargin) as Lang.Float);
|
|
mErrorIcon = Application.loadResource(Rez.Drawables.ErrorIcon) as Graphics.BitmapResource;
|
|
}
|
|
|
|
// 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(dc has :setAntiAlias) {
|
|
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 Lang.Array<ErrorView or ErrorDelegate> {
|
|
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 'updateNextMenuItem()' 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(:updateNextMenuItem), 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;
|
|
}
|
|
|
|
}
|