mirror of
				https://github.com/house-of-abbey/GarminHomeAssistant.git
				synced 2025-10-31 07:48:13 +00:00 
			
		
		
		
	use full numpad, validate 4-digit pin, add visual feedback on click, show toast on error
This commit is contained in:
		| @@ -39,7 +39,8 @@ | ||||
|   des Geräts zu schonen.</string> | ||||
|   <string id="SettingsConfirmTimeout">Nach dieser Zeit (in Sekunden) wird der Bestätigungsdialog einer Aktion geschlossen und die | ||||
|     Aktion abgebrochen. Auf 0 setzen, um den Timeout zu deaktivieren.</string> | ||||
|   <string id="SettingsPin">PIN für alle Actions mit 'confirm': true.</string> | ||||
|   <string id="SettingsPin">4-stellige PIN für alle Actions mit 'confirm': true (0000-9999).</string> | ||||
|   <string id="SettingsPinError">Bitte eine gültige 4-stellige numerische PIN in den App Einstellungen eingeben (0000-4444).</string> | ||||
|   <string id="SettingsWidgetStart">(Nur Widget) Anwendung automatisch über das Widget starten ohne drauftippen zu müssen.</string> | ||||
|   <string id="SettingsEnableBatteryLevel">Hintergrunddienst aktivieren, um den Ladezustand der Batterie an HomeAssistant zu senden.</string> | ||||
|   <string id="SettingsBatteryLevelRefreshRate">Die Aktualisierungsrate (in Minuten) mit der der Ladezustand der Batterie | ||||
|   | ||||
| @@ -42,6 +42,7 @@ | ||||
|   <string id="PotentialError">Potential Error</string> | ||||
|   <string id="PinInputLocked">PIN input locked for</string> | ||||
|   <string id="Seconds">seconds</string> | ||||
|   <string id="WrongPin">Wrong PIN</string> | ||||
|  | ||||
|   <!-- For the settings GUI --> | ||||
|   <string id="SettingsSelect">Select...</string> | ||||
| @@ -55,7 +56,8 @@ | ||||
|   <string id="SettingsAppTimeout">Timeout in seconds. Exit the application after this period of inactivity to save the device battery.</string> | ||||
|   <string id="SettingsPollDelay">Additional poll delay (in seconds). Adds a delay between the status update of all menu items.</string> | ||||
|   <string id="SettingsConfirmTimeout">After this time (in seconds), a confirmation dialog for an action is automatically closed and the action is cancelled. Set to 0 to disable the timeout.</string> | ||||
|   <string id="SettingsPin">PIN to be used for all actions that require confirmation.</string> | ||||
|   <string id="SettingsPin">4-digit PIN to be used for all actions that require confirmation (0000-9999).</string> | ||||
|   <string id="SettingsPinError">Please configure a valid 4-digit numeric PIN between 0000 and 9999 in the application settings.</string> | ||||
|   <string id="SettingsTextAlign">Left (off) or Right (on) Menu Alignment.</string> | ||||
|   <string id="LeftToRight">Left to right</string> | ||||
|   <string id="RightToLeft">Right to Left</string> | ||||
|   | ||||
| @@ -29,26 +29,33 @@ class PinDigit extends WatchUi.Selectable { | ||||
|  | ||||
|     private var mDigit as Number; | ||||
|  | ||||
|     function initialize(digit as Number, halfX as Number, halfY as Number) { | ||||
|         var margin = 40; | ||||
|         var x = (digit % 2 == 1) ? 0 + margin : halfX + margin; // place even numbers in right half, odd in left half | ||||
|         var y = (digit < 3) ? 0 + margin : halfY + margin; // place 1&2 on top half, 3&4 on bottom half | ||||
|         var width = halfX - 2 * margin; | ||||
|         var height = halfY - 2 * margin; | ||||
|     function initialize(digit as Number, stepX as Number, stepY as Number) { | ||||
|         var marginX = stepX * 0.05; // 5% margin on all sides | ||||
|         var marginY = stepY * 0.05;  | ||||
|         var x = (digit == 0) ? stepX : stepX * ((digit+2) % 3); // layout '0' in 2nd col, others ltr in 3 columns | ||||
|         x += marginX + HomeAssistantPinConfirmationView.MARGIN_X; | ||||
|         var y = (digit == 0) ? stepY * 4 : (digit <= 3) ? stepY : (digit <=6) ? stepY * 2 : stepY * 3; // layout '0' in bottom row (5), others top to bottom in 3 rows (2-4) (row 1 is reserved for masked pin) | ||||
|         y += marginY; | ||||
|         var width = stepX - (marginX * 2); | ||||
|         var height = stepY - (marginY * 2); | ||||
|  | ||||
|         // build text area | ||||
|         var textArea = new WatchUi.TextArea({ | ||||
|             :text=>digit.format("%d"), | ||||
|             :color=>Graphics.COLOR_WHITE, | ||||
|             :font=>[Graphics.FONT_NUMBER_THAI_HOT, Graphics.FONT_NUMBER_HOT, Graphics.FONT_NUMBER_MEDIUM, Graphics.FONT_NUMBER_MILD], | ||||
|         var button = new PinDigitButton({ | ||||
|             :width=>width, | ||||
|             :height=>height, | ||||
|             :justification=>Graphics.TEXT_JUSTIFY_CENTER | ||||
|             :label=>digit | ||||
|         }); | ||||
|  | ||||
|         var buttonTouched = new PinDigitButton({ | ||||
|             :width=>width, | ||||
|             :height=>height, | ||||
|             :label=>digit, | ||||
|             :touched=>true | ||||
|         }); | ||||
|  | ||||
|         // initialize selectable | ||||
|         Selectable.initialize({ | ||||
|             :stateDefault=>textArea, | ||||
|             :stateDefault=>button, | ||||
|             :stateHighlighted=>buttonTouched, | ||||
|             :locX =>x, | ||||
|             :locY=>y, | ||||
|             :width=>width, | ||||
| @@ -63,34 +70,69 @@ class PinDigit extends WatchUi.Selectable { | ||||
|         return mDigit; | ||||
|     } | ||||
|  | ||||
|     class PinDigitButton extends WatchUi.Drawable { | ||||
|         private var mText as Number; | ||||
|         private var mTouched as Boolean = false; | ||||
|  | ||||
|         function initialize(options) { | ||||
|             Drawable.initialize(options); | ||||
|             mText = options.get(:label); | ||||
|             mTouched = options.get(:touched); | ||||
|         } | ||||
|  | ||||
|         function draw(dc) { | ||||
|             if (mTouched) { | ||||
|                 dc.setColor(Graphics.COLOR_ORANGE, Graphics.COLOR_ORANGE); | ||||
|             } else { | ||||
|                 dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_DK_GRAY); | ||||
|             } | ||||
|             dc.fillCircle(locX + width / 2, locY + height / 2, height / 2); // circle fill | ||||
|             dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_LT_GRAY); | ||||
|             dc.setPenWidth(3); | ||||
|             dc.drawCircle(locX + width / 2, locY + height / 2, height / 2); // circle outline | ||||
|             dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); | ||||
|             dc.drawText(locX+width / 2, locY+height / 2, Graphics.FONT_TINY, mText, Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER); // center text in circle | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| class HomeAssistantPinConfirmationView extends WatchUi.View { | ||||
|  | ||||
|     static const MARGIN_X = 20; // margin on left & right side of screen (overall prettier and works better on round displays) | ||||
|  | ||||
|     var mPinMask as String = ""; | ||||
|          | ||||
|     function initialize() { | ||||
|         View.initialize(); | ||||
|     } | ||||
|  | ||||
|     function onLayout(dc as Dc) as Void { | ||||
|         var halfX = dc.getWidth()/2; | ||||
|         var halfY = dc.getHeight()/2; | ||||
|         var stepX = (dc.getWidth() - MARGIN_X * 2) / 3;   // three columns | ||||
|         var stepY = dc.getHeight() / 5;                   // five rows (first row for masked pin entry) | ||||
|         var digits = []; | ||||
|         for (var i=0; i<=9; i++) { | ||||
|             digits.add(new PinDigit(i, stepX, stepY)); | ||||
|         } | ||||
|         // draw digits | ||||
|         setLayout([ | ||||
|             new PinDigit(1, halfX, halfY), | ||||
|             new PinDigit(2, halfX, halfY), | ||||
|             new PinDigit(3, halfX, halfY), | ||||
|             new PinDigit(4, halfX, halfY) | ||||
|         ]); | ||||
|         setLayout(digits); | ||||
|     } | ||||
|  | ||||
|     function onUpdate(dc as Dc) as Void { | ||||
|         View.onUpdate(dc); | ||||
|         // draw cross | ||||
|         var halfX = dc.getWidth()/2; | ||||
|         var halfY = dc.getHeight()/2; | ||||
|         if (mPinMask.length() != 0) { | ||||
|             dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK); | ||||
|         dc.drawRectangle(halfX, dc.getHeight() * 0.1, 2, dc.getHeight() * 0.8); | ||||
|         dc.drawRectangle(dc.getWidth() * 0.1, halfY, dc.getWidth() * 0.8, 2); | ||||
|             dc.drawText(dc.getWidth()/2, dc.getHeight()/10, Graphics.FONT_SYSTEM_SMALL, mPinMask, Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function updatePinMask(length as Number) { | ||||
|         mPinMask = ""; | ||||
|         for (var i=0; i<length; i++) { | ||||
|             mPinMask += "*"; | ||||
|         } | ||||
|         requestUpdate(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -104,8 +146,9 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { | ||||
|     private var mTimer         as Timer.Timer or Null; | ||||
|     private var mState         as Lang.Boolean; | ||||
|     private var mFailures      as PinFailures; | ||||
|     private var mView          as HomeAssistantPinConfirmationView; | ||||
|  | ||||
|     function initialize(callback as Method(state as Lang.Boolean) as Void, state as Lang.Boolean, pin as String) { | ||||
|     function initialize(callback as Method(state as Lang.Boolean) as Void, state as Lang.Boolean, pin as String, view as HomeAssistantPinConfirmationView) { | ||||
|         BehaviorDelegate.initialize(); | ||||
|         mFailures      = new PinFailures(); | ||||
|         if (mFailures.isLocked()) { | ||||
| @@ -118,6 +161,7 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { | ||||
|         mEnteredPin    = ""; | ||||
|         mConfirmMethod = callback; | ||||
|         mState         = state; | ||||
|         mView          = view; | ||||
|         resetTimer(); | ||||
|     } | ||||
|  | ||||
| @@ -127,10 +171,8 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { | ||||
|         } | ||||
|         var instance = event.getInstance(); | ||||
|         if (instance instanceof PinDigit && event.getPreviousState() == :stateSelected) { | ||||
|             if (Attention has :vibrate && Settings.getVibrate()) { | ||||
|                 Attention.vibrate([new Attention.VibeProfile(25, 25)]); | ||||
|             } | ||||
|             mEnteredPin += instance.getDigit(); | ||||
|             createUserFeedback(); | ||||
|             // System.println("HomeAssitantPinConfirmationDelegate onSelectable() mEnteredPin = " + mEnteredPin); | ||||
|             if (mEnteredPin.length() == mPin.length()) { | ||||
|                 if (mEnteredPin.equals(mPin)) { | ||||
| @@ -151,6 +193,13 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     function createUserFeedback() { | ||||
|         if (Attention has :vibrate && Settings.getVibrate()) { | ||||
|             Attention.vibrate([new Attention.VibeProfile(25, 25)]); | ||||
|         } | ||||
|         mView.updatePinMask(mEnteredPin.length()); | ||||
|     } | ||||
|  | ||||
|     function resetTimer() { | ||||
|         var timeout = Settings.getConfirmTimeout(); // ms | ||||
|         if (timeout > 0) { | ||||
| @@ -159,7 +208,7 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { | ||||
|             } else { | ||||
|                 mTimer = new Timer.Timer(); | ||||
|             } | ||||
|             mTimer.start(method(:goBack), timeout, true); | ||||
|             mTimer.start(method(:goBack), timeout, false); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -184,6 +233,9 @@ class HomeAssistantPinConfirmationDelegate extends WatchUi.BehaviorDelegate { | ||||
|                 new Attention.VibeProfile(25, 100) | ||||
|             ]); | ||||
|         } | ||||
|         if (WatchUi has :showToast) { | ||||
|             showToast($.Rez.Strings.WrongPin, null); | ||||
|         } | ||||
|         goBack(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -94,9 +94,14 @@ class HomeAssistantTapMenuItem extends WatchUi.IconMenuItem { | ||||
|                     WatchUi.SLIDE_IMMEDIATE | ||||
|                 ); | ||||
|             } else { | ||||
|                 if (pin.toNumber() == null || pin.length() != 4) { | ||||
|                     ErrorView.show(WatchUi.loadResource($.Rez.Strings.SettingsPinError) as Lang.String); | ||||
|                     return; | ||||
|                 } | ||||
|                 var pinConfirmationView = new HomeAssistantPinConfirmationView(); | ||||
|                 WatchUi.pushView( | ||||
|                     new HomeAssistantPinConfirmationView(), | ||||
|                     new HomeAssistantPinConfirmationDelegate(method(:onConfirm), false, pin), | ||||
|                     pinConfirmationView, | ||||
|                     new HomeAssistantPinConfirmationDelegate(method(:onConfirm), false, pin, pinConfirmationView), | ||||
|                     WatchUi.SLIDE_IMMEDIATE | ||||
|                 ); | ||||
|             } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user