From d2e1e198c885b2120feb2e06bc47f4688bddeb68 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 16:51:23 +1100 Subject: [PATCH 01/66] feat(winforms): location support --- .../src/toga_winforms/hardware/location.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 winforms/src/toga_winforms/hardware/location.py diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py new file mode 100644 index 0000000000..8710e746b1 --- /dev/null +++ b/winforms/src/toga_winforms/hardware/location.py @@ -0,0 +1,62 @@ +from __future__ import annotations +from concurrent.futures import Future + +import clr + +clr.AddReference("System.Device") + +from System.Device.Location import ( + GeoCoordinateWatcher, + GeoPositionAccuracy, + GeoPositionChangedEventArgs, + GeoCoordinate, +) +from System import EventHandler +from toga import LatLng + + +def toga_location(location: GeoCoordinate): + """Convert a GeoCoordinate into a Toga LatLng and altitude.""" + + return { + "location": LatLng(location.Latitude, location.Longitude), + "altitude": location.Altitude, + "speed": location.Speed, + } + + +class Location: + def __init__(self, interface): + self.watcher = GeoCoordinateWatcher(GeoPositionAccuracy.Default) + self.watcher.add_PositionChanged( + EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( + self._position_changed + ) + ) + self.watcher.Start() + + def _position_changed( + self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] + ): + self._position = event.Position.Location + + def has_permission(self): + return True + + def has_background_permission(self): + return True + + def request_permission(self, future: Future): + future.set_result(True) + + def request_background_permission(self, future: Future): + future.set_result(True) + + def current_location(self, result: Future): + result.set_result(toga_location(self._position)) + + def start_tracking(self): + self.watcher.Start() + + def stop_tracking(self): + self.watcher.Stop() From edc1e02619d3dd028a82060fd096dae74ee34066 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 14:27:20 +0800 Subject: [PATCH 02/66] chore: isort --- winforms/src/toga_winforms/hardware/location.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 8710e746b1..43434910fd 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -1,17 +1,19 @@ from __future__ import annotations + from concurrent.futures import Future import clr clr.AddReference("System.Device") +from System import EventHandler from System.Device.Location import ( + GeoCoordinate, GeoCoordinateWatcher, GeoPositionAccuracy, GeoPositionChangedEventArgs, - GeoCoordinate, ) -from System import EventHandler + from toga import LatLng From 8a1fcb13f1380cb00158711bf078bc37111f41be Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 14:30:14 +0800 Subject: [PATCH 03/66] chore: move AddReference call --- winforms/src/toga_winforms/hardware/__init__.py | 3 +++ winforms/src/toga_winforms/hardware/location.py | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/__init__.py b/winforms/src/toga_winforms/hardware/__init__.py index e69de29bb2..ca55608a49 100644 --- a/winforms/src/toga_winforms/hardware/__init__.py +++ b/winforms/src/toga_winforms/hardware/__init__.py @@ -0,0 +1,3 @@ +import clr + +clr.AddReference("System.Device") diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 43434910fd..8f3e3f065a 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -2,10 +2,6 @@ from concurrent.futures import Future -import clr - -clr.AddReference("System.Device") - from System import EventHandler from System.Device.Location import ( GeoCoordinate, From 6b222564a570187b1b6534c736ad70e0ec6a0945 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 14:34:51 +0800 Subject: [PATCH 04/66] chore: change log entry --- changes/2979.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/2979.feature.rst diff --git a/changes/2979.feature.rst b/changes/2979.feature.rst new file mode 100644 index 0000000000..779ca2a14b --- /dev/null +++ b/changes/2979.feature.rst @@ -0,0 +1 @@ +Location support is now available on Windows From 80fd37fca8536ad95fe4c602e1d0807ce2c8ca27 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 17:39:57 +1100 Subject: [PATCH 05/66] Update test_location.py --- testbed/tests/hardware/test_location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbed/tests/hardware/test_location.py b/testbed/tests/hardware/test_location.py index 1131676c52..d093b9ca7f 100644 --- a/testbed/tests/hardware/test_location.py +++ b/testbed/tests/hardware/test_location.py @@ -10,7 +10,7 @@ @pytest.fixture async def location_probe(monkeypatch, app_probe): - skip_on_platforms("linux", "windows") + skip_on_platforms("linux") probe = get_probe(monkeypatch, app_probe, "Location") yield probe probe.cleanup() From 2f5c2e85804f1ca71f67b455332bdcca4d143b3c Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 15:02:58 +0800 Subject: [PATCH 06/66] probe --- winforms/tests_backend/hardware/__init__.py | 0 winforms/tests_backend/hardware/location.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 winforms/tests_backend/hardware/__init__.py create mode 100644 winforms/tests_backend/hardware/location.py diff --git a/winforms/tests_backend/hardware/__init__.py b/winforms/tests_backend/hardware/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py new file mode 100644 index 0000000000..e69de29bb2 From 2b6d4274860941e1c3254759c6fd04552cbffd58 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 19:16:40 +1100 Subject: [PATCH 07/66] chore: export Location class --- winforms/src/toga_winforms/factory.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/winforms/src/toga_winforms/factory.py b/winforms/src/toga_winforms/factory.py index de24bc1f62..e81adf5f9d 100644 --- a/winforms/src/toga_winforms/factory.py +++ b/winforms/src/toga_winforms/factory.py @@ -4,6 +4,7 @@ from .app import App from .command import Command from .fonts import Font +from .hardware.location import Location from .icons import Icon from .images import Image from .paths import Paths @@ -61,6 +62,7 @@ def not_implemented(feature): "Divider", "ImageView", "Label", + "Location", "MapView", "MultilineTextInput", "NumberInput", From 146e9c4211caa792da48a1b4a131f79c7cd1e710 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 19:20:54 +1100 Subject: [PATCH 08/66] test: probes --- winforms/tests_backend/hardware/hardware.py | 9 ++++++++ winforms/tests_backend/hardware/location.py | 24 +++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 winforms/tests_backend/hardware/hardware.py diff --git a/winforms/tests_backend/hardware/hardware.py b/winforms/tests_backend/hardware/hardware.py new file mode 100644 index 0000000000..9516e574b8 --- /dev/null +++ b/winforms/tests_backend/hardware/hardware.py @@ -0,0 +1,9 @@ +from ..app import AppProbe + + +class HardwareProbe(AppProbe): + + def __init__(self, monkeypatch, app_probe): + super().__init__(app_probe.app) + + self.monkeypatch = monkeypatch diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index e69de29bb2..11afe4d700 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -0,0 +1,24 @@ +from .hardware import HardwareProbe + + +class LocationProbe(HardwareProbe): + def cleanup(self): + pass + + def allow_permissions(self): + pass + + def allow_permission(self): + pass + + def grant_permission(self): + pass + + def reject_permission(self): + pass + + def add_location(self, loc): + pass + + def allow_background_permission(self): + pass From 2823a852f338460e38901943cb238b0a689121e1 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 21:40:54 +1100 Subject: [PATCH 09/66] chore: use futures better --- winforms/src/toga_winforms/hardware/location.py | 9 +++++++-- winforms/tests_backend/hardware/location.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 8f3e3f065a..1f0a6a5a6c 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -24,6 +24,8 @@ def toga_location(location: GeoCoordinate): class Location: + _location: Future + def __init__(self, interface): self.watcher = GeoCoordinateWatcher(GeoPositionAccuracy.Default) self.watcher.add_PositionChanged( @@ -32,11 +34,12 @@ def __init__(self, interface): ) ) self.watcher.Start() + self._location = Future() def _position_changed( self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] ): - self._position = event.Position.Location + self._location.set_result(event.Position.Location) def has_permission(self): return True @@ -51,7 +54,9 @@ def request_background_permission(self, future: Future): future.set_result(True) def current_location(self, result: Future): - result.set_result(toga_location(self._position)) + self._location.add_done_callback( + lambda value: result.set_result(toga_location(value)) + ) def start_tracking(self): self.watcher.Start() diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 11afe4d700..d58cc8a107 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -17,7 +17,7 @@ def grant_permission(self): def reject_permission(self): pass - def add_location(self, loc): + def add_location(self, loc, *args): pass def allow_background_permission(self): From 3c6a121280aac09e33bdad55acc314bf34f41cae Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 23:35:37 +1100 Subject: [PATCH 10/66] make permission settable --- winforms/src/toga_winforms/hardware/location.py | 5 +++-- winforms/tests_backend/hardware/location.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 1f0a6a5a6c..03a6d95ebb 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -35,6 +35,7 @@ def __init__(self, interface): ) self.watcher.Start() self._location = Future() + self._has_permission = True def _position_changed( self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] @@ -42,10 +43,10 @@ def _position_changed( self._location.set_result(event.Position.Location) def has_permission(self): - return True + return self._has_permission def has_background_permission(self): - return True + return self._has_permission def request_permission(self, future: Future): future.set_result(True) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index d58cc8a107..b9f98d6ea6 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -12,10 +12,10 @@ def allow_permission(self): pass def grant_permission(self): - pass + self.app.location._has_permission = True def reject_permission(self): - pass + self.app.location._has_permission = False def add_location(self, loc, *args): pass From 7de79b52aef58f290b2e8201ec5daa2b55c46977 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 23:38:55 +1100 Subject: [PATCH 11/66] timeout --- winforms/src/toga_winforms/hardware/location.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 03a6d95ebb..c1dd1e23b3 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -55,9 +55,7 @@ def request_background_permission(self, future: Future): future.set_result(True) def current_location(self, result: Future): - self._location.add_done_callback( - lambda value: result.set_result(toga_location(value)) - ) + result.set_result(toga_location(self._location.result(timeout=5))) def start_tracking(self): self.watcher.Start() From 8aab6b4bf5292e4a6581d3756ffe49811ac8f20f Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Nov 2024 23:39:57 +1100 Subject: [PATCH 12/66] add missing methods --- winforms/tests_backend/hardware/location.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index b9f98d6ea6..5dad685b4a 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -3,7 +3,12 @@ class LocationProbe(HardwareProbe): def cleanup(self): - pass + # Delete the location service instance. This ensures that a freshly mocked + # LocationManager is installed for each test. + try: + del self.app._location + except AttributeError: + pass def allow_permissions(self): pass @@ -22,3 +27,9 @@ def add_location(self, loc, *args): def allow_background_permission(self): pass + + async def simulate_location_error(self, loco): + raise RuntimeError(f"Unable to obtain a location ({loco})") + + async def simulate_current_location(self, loco): + return await loco From 4dbd6c33d9070ee672ba39aff2342f54e68c800a Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 25 Nov 2024 16:30:33 +0800 Subject: [PATCH 13/66] improve types --- winforms/src/toga_winforms/hardware/location.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index c1dd1e23b3..217d36ff3e 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -10,7 +10,7 @@ GeoPositionChangedEventArgs, ) -from toga import LatLng +from toga import AsyncResult, LatLng def toga_location(location: GeoCoordinate): @@ -48,17 +48,17 @@ def has_permission(self): def has_background_permission(self): return self._has_permission - def request_permission(self, future: Future): + def request_permission(self, future: AsyncResult[bool]) -> None: future.set_result(True) - def request_background_permission(self, future: Future): + def request_background_permission(self, future: AsyncResult[bool]) -> None: future.set_result(True) - def current_location(self, result: Future): + def current_location(self, result: AsyncResult[dict]) -> None: result.set_result(toga_location(self._location.result(timeout=5))) - def start_tracking(self): + def start_tracking(self) -> None: self.watcher.Start() - def stop_tracking(self): + def stop_tracking(self) -> None: self.watcher.Stop() From 12668496a3a9d6d17ee11297f8000f19624a4699 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 25 Nov 2024 08:00:12 +1100 Subject: [PATCH 14/66] add missing functions --- winforms/tests_backend/hardware/location.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 5dad685b4a..5f7e1e9e92 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -22,7 +22,7 @@ def grant_permission(self): def reject_permission(self): self.app.location._has_permission = False - def add_location(self, loc, *args): + def add_location(self, location, altitude, cached=False): pass def allow_background_permission(self): @@ -33,3 +33,6 @@ async def simulate_location_error(self, loco): async def simulate_current_location(self, loco): return await loco + + async def simulate_location_update(self): + pass From 9087b15d54bcd5ce1fc4e0b10ad5b147ec820825 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 25 Nov 2024 17:18:02 +1100 Subject: [PATCH 15/66] reject_permission not supported --- winforms/tests_backend/hardware/location.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 5f7e1e9e92..4da318ab6f 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -1,3 +1,5 @@ +from pytest import xfail + from .hardware import HardwareProbe @@ -20,7 +22,7 @@ def grant_permission(self): self.app.location._has_permission = True def reject_permission(self): - self.app.location._has_permission = False + xfail("No support for permissions here") def add_location(self, location, altitude, cached=False): pass From 8bce6301d0d71b6de5d676c6e6daf24f5d401c13 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 25 Nov 2024 17:22:50 +1100 Subject: [PATCH 16/66] fix import --- winforms/src/toga_winforms/hardware/location.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 217d36ff3e..a133118fa9 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -10,7 +10,8 @@ GeoPositionChangedEventArgs, ) -from toga import AsyncResult, LatLng +from toga import LatLng +from toga.handlers import AsyncResult def toga_location(location: GeoCoordinate): From 63e1000d87ce4ab290482e80346acb18b2ab58f1 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 26 Nov 2024 10:18:27 +1100 Subject: [PATCH 17/66] Update widgets_by_platform.csv --- docs/reference/data/widgets_by_platform.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/data/widgets_by_platform.csv b/docs/reference/data/widgets_by_platform.csv index f3108e1543..b1c6128766 100644 --- a/docs/reference/data/widgets_by_platform.csv +++ b/docs/reference/data/widgets_by_platform.csv @@ -30,7 +30,7 @@ ScrollContainer,Layout Widget,:class:`~toga.ScrollContainer`,A container that ca SplitContainer,Layout Widget,:class:`~toga.SplitContainer`,A container that divides an area into two panels with a movable border,|y|,|y|,|y|,,,, OptionContainer,Layout Widget,:class:`~toga.OptionContainer`,A container that can display multiple labeled tabs of content,|y|,|y|,|y|,|y|,|y|,, Camera,Hardware,:class:`~toga.hardware.camera.Camera`,A sensor that can capture photos and/or video.,|y|,,,|y|,|y|,, -Location,Hardware,:class:`~toga.hardware.location.Location`,A sensor that can capture the geographical location of the device.,|y|,,,|y|,|y|,, +Location,Hardware,:class:`~toga.hardware.location.Location`,A sensor that can capture the geographical location of the device.,|y|,,|y|,|y|,|y|,, Screen,Hardware,:class:`~toga.screens.Screen`,A representation of a screen attached to a device.,|y|,|y|,|y|,|y|,|y|,|b|,|b| App Paths,Resource,:class:`~toga.paths.Paths`,A mechanism for obtaining platform-appropriate filesystem locations for an application.,|y|,|y|,|y|,|y|,|y|,,|b| Command,Resource,:class:`~toga.Command`,Command,|y|,|y|,|y|,,|y|,, From 75c43763564071ab235c79010060c275c6749eb2 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 1 Dec 2024 09:44:35 +0800 Subject: [PATCH 18/66] don't track by default --- winforms/src/toga_winforms/hardware/location.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index a133118fa9..ab97085311 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -29,11 +29,10 @@ class Location: def __init__(self, interface): self.watcher = GeoCoordinateWatcher(GeoPositionAccuracy.Default) - self.watcher.add_PositionChanged( - EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( - self._position_changed - ) + self._handler = EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( + self._position_changed ) + self.watcher.Start() self._location = Future() self._has_permission = True @@ -59,7 +58,7 @@ def current_location(self, result: AsyncResult[dict]) -> None: result.set_result(toga_location(self._location.result(timeout=5))) def start_tracking(self) -> None: - self.watcher.Start() + self.watcher.add_PositionChanged(self._handler) def stop_tracking(self) -> None: - self.watcher.Stop() + self.watcher.remove_PositionChanged(self._handler) From a719a586a845a1c1473a3c8807f0f771fb7613ec Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 1 Dec 2024 09:45:53 +0800 Subject: [PATCH 19/66] get location directly from watcher --- winforms/src/toga_winforms/hardware/location.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index ab97085311..3829908035 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -1,7 +1,5 @@ from __future__ import annotations -from concurrent.futures import Future - from System import EventHandler from System.Device.Location import ( GeoCoordinate, @@ -25,8 +23,6 @@ def toga_location(location: GeoCoordinate): class Location: - _location: Future - def __init__(self, interface): self.watcher = GeoCoordinateWatcher(GeoPositionAccuracy.Default) self._handler = EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( @@ -34,7 +30,6 @@ def __init__(self, interface): ) self.watcher.Start() - self._location = Future() self._has_permission = True def _position_changed( @@ -55,7 +50,7 @@ def request_background_permission(self, future: AsyncResult[bool]) -> None: future.set_result(True) def current_location(self, result: AsyncResult[dict]) -> None: - result.set_result(toga_location(self._location.result(timeout=5))) + result.set_result(toga_location(self.watcher.Position.Location)) def start_tracking(self) -> None: self.watcher.add_PositionChanged(self._handler) From 35217f0c05e4d191496da35fa6feed1771603c7c Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 1 Dec 2024 09:46:25 +0800 Subject: [PATCH 20/66] call on_change callback if available --- winforms/src/toga_winforms/hardware/location.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 3829908035..dadfacde96 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -15,6 +15,9 @@ def toga_location(location: GeoCoordinate): """Convert a GeoCoordinate into a Toga LatLng and altitude.""" + if location.IsUnknown: + return None + return { "location": LatLng(location.Latitude, location.Longitude), "altitude": location.Altitude, @@ -35,7 +38,9 @@ def __init__(self, interface): def _position_changed( self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] ): - self._location.set_result(event.Position.Location) + location = toga_location(event.Position.Location) + if location: + self.interface.on_change(**location) def has_permission(self): return self._has_permission From 714e7f39f94c390e243b490452bbe86c5da5a619 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 1 Dec 2024 09:58:07 +0800 Subject: [PATCH 21/66] call on_change from probe --- winforms/tests_backend/hardware/location.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 4da318ab6f..3884411624 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -1,5 +1,9 @@ +from unittest.mock import Mock + from pytest import xfail +from toga.types import LatLng + from .hardware import HardwareProbe @@ -24,8 +28,15 @@ def grant_permission(self): def reject_permission(self): xfail("No support for permissions here") - def add_location(self, location, altitude, cached=False): - pass + def add_location(self, location: LatLng, altitude, cached=False): + m = Mock() + m.Position = Mock() + m.Position.Location = Mock() + m.Position.Location.Latitude = location.lat + m.Position.Location.Longitude = location.lng + m.Position.Location.Altitude = altitude + + self.app.location._impl._position_changed(None, m) def allow_background_permission(self): pass From 7728c865c192d7f1bbb26ce1d1a4eabca64c9954 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 2 Dec 2024 07:37:34 +0000 Subject: [PATCH 22/66] raise PermissionError if foreground not granted --- winforms/src/toga_winforms/hardware/location.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index dadfacde96..ff3d5c52c0 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -52,6 +52,8 @@ def request_permission(self, future: AsyncResult[bool]) -> None: future.set_result(True) def request_background_permission(self, future: AsyncResult[bool]) -> None: + if not self.has_permission(): + raise PermissionError() future.set_result(True) def current_location(self, result: AsyncResult[dict]) -> None: From 704059c91ef730811be9d6041d53ff1356a6356f Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 2 Dec 2024 07:37:49 +0000 Subject: [PATCH 23/66] sanity --- winforms/tests_backend/hardware/location.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 3884411624..b1dcbc65f2 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -45,7 +45,9 @@ async def simulate_location_error(self, loco): raise RuntimeError(f"Unable to obtain a location ({loco})") async def simulate_current_location(self, loco): - return await loco + res = await loco + assert res is not None + return res async def simulate_location_update(self): pass From 3a6b1e974cb21a11f7194676d8acf8749dcad13d Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 2 Dec 2024 07:45:59 +0000 Subject: [PATCH 24/66] fill out simulate_location --- winforms/tests_backend/hardware/location.py | 23 ++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index b1dcbc65f2..b60c74adc6 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -8,6 +8,10 @@ class LocationProbe(HardwareProbe): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._locations = [] + def cleanup(self): # Delete the location service instance. This ensures that a freshly mocked # LocationManager is installed for each test. @@ -36,7 +40,11 @@ def add_location(self, location: LatLng, altitude, cached=False): m.Position.Location.Longitude = location.lng m.Position.Location.Altitude = altitude - self.app.location._impl._position_changed(None, m) + self._locations.append(m) + + def reset_locations(self): + # self._cached_location = None + self._locations = [] def allow_background_permission(self): pass @@ -44,10 +52,15 @@ def allow_background_permission(self): async def simulate_location_error(self, loco): raise RuntimeError(f"Unable to obtain a location ({loco})") - async def simulate_current_location(self, loco): - res = await loco - assert res is not None - return res + async def simulate_current_location(self, location): + await self.redraw("Wait for current location") + if not self._cached_location: + # Trigger the callback + self.app.location._impl._position_changed(None, self._locations[-1]) + + self.reset_locations() + + return await location async def simulate_location_update(self): pass From 7d35190723e65da956948afdcb5e16935e76d2a6 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 2 Dec 2024 15:58:58 +0800 Subject: [PATCH 25/66] Init cached field --- winforms/tests_backend/hardware/location.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index b60c74adc6..5fe5552435 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -10,7 +10,7 @@ class LocationProbe(HardwareProbe): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._locations = [] + self.reset_locations() def cleanup(self): # Delete the location service instance. This ensures that a freshly mocked @@ -43,7 +43,7 @@ def add_location(self, location: LatLng, altitude, cached=False): self._locations.append(m) def reset_locations(self): - # self._cached_location = None + self._cached_location = None self._locations = [] def allow_background_permission(self): From 67ad19012a9b1b52aae68a8cfe76da6f8e9551db Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 2 Dec 2024 16:21:06 +0800 Subject: [PATCH 26/66] override watcher impl --- winforms/tests_backend/hardware/location.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 5fe5552435..bc75855bfa 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -10,6 +10,7 @@ class LocationProbe(HardwareProbe): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.app.location._impl.watcher = Mock() self.reset_locations() def cleanup(self): @@ -55,6 +56,7 @@ async def simulate_location_error(self, loco): async def simulate_current_location(self, location): await self.redraw("Wait for current location") if not self._cached_location: + self.app.location._impl.watcher.Position = self._locations[-1] # Trigger the callback self.app.location._impl._position_changed(None, self._locations[-1]) From 312e51c8782ad11c5aa3098a06e461fe907cac8d Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 2 Dec 2024 23:59:16 +0800 Subject: [PATCH 27/66] add missing interface field --- winforms/src/toga_winforms/hardware/location.py | 1 + 1 file changed, 1 insertion(+) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index ff3d5c52c0..fbe64f2c49 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -27,6 +27,7 @@ def toga_location(location: GeoCoordinate): class Location: def __init__(self, interface): + self.interface = interface self.watcher = GeoCoordinateWatcher(GeoPositionAccuracy.Default) self._handler = EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( self._position_changed From c078b7e0f5ea7f80d27ccc28f6e3c26a016553a8 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 00:23:23 +0800 Subject: [PATCH 28/66] remove dud field --- winforms/src/toga_winforms/hardware/location.py | 1 - 1 file changed, 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index fbe64f2c49..5dccb9d84e 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -21,7 +21,6 @@ def toga_location(location: GeoCoordinate): return { "location": LatLng(location.Latitude, location.Longitude), "altitude": location.Altitude, - "speed": location.Speed, } From 76eb2b05119eeb4d0802f0c53a611b82335b5a71 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 00:25:39 +0800 Subject: [PATCH 29/66] set IsUnknown --- winforms/tests_backend/hardware/location.py | 1 + 1 file changed, 1 insertion(+) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index bc75855bfa..57221edd48 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -37,6 +37,7 @@ def add_location(self, location: LatLng, altitude, cached=False): m = Mock() m.Position = Mock() m.Position.Location = Mock() + m.Position.Location.IsUnknown = False m.Position.Location.Latitude = location.lat m.Position.Location.Longitude = location.lng m.Position.Location.Altitude = altitude From 0faf901483ed6172955d08c23e48519b7a46407b Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 00:26:06 +0800 Subject: [PATCH 30/66] set watcher position --- winforms/tests_backend/hardware/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 57221edd48..7e7fc9ab4e 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -43,6 +43,7 @@ def add_location(self, location: LatLng, altitude, cached=False): m.Position.Location.Altitude = altitude self._locations.append(m) + self.app.location._impl.watcher.Position = m.Position def reset_locations(self): self._cached_location = None @@ -57,7 +58,6 @@ async def simulate_location_error(self, loco): async def simulate_current_location(self, location): await self.redraw("Wait for current location") if not self._cached_location: - self.app.location._impl.watcher.Position = self._locations[-1] # Trigger the callback self.app.location._impl._position_changed(None, self._locations[-1]) From 12294c51b05c02a2174c7cf8d3f110346d184609 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 00:26:31 +0800 Subject: [PATCH 31/66] set spec --- winforms/tests_backend/hardware/location.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 7e7fc9ab4e..95d968a7f0 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -1,6 +1,9 @@ from unittest.mock import Mock from pytest import xfail +from System.Device.Location import ( + GeoCoordinate, +) from toga.types import LatLng @@ -34,7 +37,7 @@ def reject_permission(self): xfail("No support for permissions here") def add_location(self, location: LatLng, altitude, cached=False): - m = Mock() + m = Mock(spec=GeoCoordinate) m.Position = Mock() m.Position.Location = Mock() m.Position.Location.IsUnknown = False From ace1c5f609e027d31620d3845ebf12e62c95103d Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 00:28:23 +0800 Subject: [PATCH 32/66] impl simulate_location_update --- winforms/tests_backend/hardware/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 95d968a7f0..daafd404dd 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -69,4 +69,4 @@ async def simulate_current_location(self, location): return await location async def simulate_location_update(self): - pass + self.app.location._impl._position_changed(None, self._locations[-1]) From daa168be3b00cf30d42a03d185a4b9a1a60a8610 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 03:29:07 +0000 Subject: [PATCH 33/66] add lookup --- winforms/src/toga_winforms/hardware/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 5dccb9d84e..ce77caf5ec 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -57,7 +57,7 @@ def request_background_permission(self, future: AsyncResult[bool]) -> None: future.set_result(True) def current_location(self, result: AsyncResult[dict]) -> None: - result.set_result(toga_location(self.watcher.Position.Location)) + result.set_result(toga_location(self.watcher.Position.Location)["location"]) def start_tracking(self) -> None: self.watcher.add_PositionChanged(self._handler) From d43b4ad2838a34903c17e6c39af7621fefcf918b Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 05:00:25 +0000 Subject: [PATCH 34/66] remove dud hook --- winforms/tests_backend/hardware/location.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index daafd404dd..875d467afe 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -60,9 +60,6 @@ async def simulate_location_error(self, loco): async def simulate_current_location(self, location): await self.redraw("Wait for current location") - if not self._cached_location: - # Trigger the callback - self.app.location._impl._position_changed(None, self._locations[-1]) self.reset_locations() From 909775da86acfd2090a876517bfda829375c657f Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 05:38:00 +0000 Subject: [PATCH 35/66] change permission default --- winforms/src/toga_winforms/hardware/location.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index ce77caf5ec..4c79e4f8bf 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -33,7 +33,7 @@ def __init__(self, interface): ) self.watcher.Start() - self._has_permission = True + self._has_permission = False def _position_changed( self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] @@ -50,6 +50,7 @@ def has_background_permission(self): def request_permission(self, future: AsyncResult[bool]) -> None: future.set_result(True) + self._has_permission = True def request_background_permission(self, future: AsyncResult[bool]) -> None: if not self.has_permission(): From cd0f234f320907e132bcaf82ff4a71ab021d4a8d Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 05:38:21 +0000 Subject: [PATCH 36/66] restore _has_background_permission --- winforms/src/toga_winforms/hardware/location.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 4c79e4f8bf..68b0b9866d 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -34,6 +34,7 @@ def __init__(self, interface): self.watcher.Start() self._has_permission = False + self._has_background_permission = False def _position_changed( self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] @@ -46,7 +47,7 @@ def has_permission(self): return self._has_permission def has_background_permission(self): - return self._has_permission + return self._has_background_permission def request_permission(self, future: AsyncResult[bool]) -> None: future.set_result(True) @@ -56,6 +57,7 @@ def request_background_permission(self, future: AsyncResult[bool]) -> None: if not self.has_permission(): raise PermissionError() future.set_result(True) + self._has_background_permission = True def current_location(self, result: AsyncResult[dict]) -> None: result.set_result(toga_location(self.watcher.Position.Location)["location"]) From 9567bb5615c48ad0184cb1f617a49c6679190e7f Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 06:45:28 +0000 Subject: [PATCH 37/66] implement simulate_location_error properly --- winforms/tests_backend/hardware/location.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 875d467afe..7875dc9522 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -56,7 +56,9 @@ def allow_background_permission(self): pass async def simulate_location_error(self, loco): - raise RuntimeError(f"Unable to obtain a location ({loco})") + await self.redraw("Wait for location error") + + xfail("Winforms's location service doesn't raise errors on failure") async def simulate_current_location(self, location): await self.redraw("Wait for current location") From d16f780852030cae34c3ac9418dbccfdad8e7054 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 06:49:32 +0000 Subject: [PATCH 38/66] fix assignment --- winforms/tests_backend/hardware/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 7875dc9522..fd4d9e3d4b 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -31,7 +31,7 @@ def allow_permission(self): pass def grant_permission(self): - self.app.location._has_permission = True + self.app.location._impl._has_permission = True def reject_permission(self): xfail("No support for permissions here") From b6360ec50857bac075861db1a703257ed03c83dd Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 06:51:05 +0000 Subject: [PATCH 39/66] remove dud field --- winforms/tests_backend/hardware/location.py | 1 - 1 file changed, 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index fd4d9e3d4b..5e810a631d 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -49,7 +49,6 @@ def add_location(self, location: LatLng, altitude, cached=False): self.app.location._impl.watcher.Position = m.Position def reset_locations(self): - self._cached_location = None self._locations = [] def allow_background_permission(self): From 01dcc100e6585bc4083031face9ecb3e25e2608b Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 06:51:19 +0000 Subject: [PATCH 40/66] add docstring --- winforms/tests_backend/hardware/location.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 5e810a631d..2f175ea751 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -52,6 +52,9 @@ def reset_locations(self): self._locations = [] def allow_background_permission(self): + """ + we have access by default with winforms + """ pass async def simulate_location_error(self, loco): From da71e61e7ce7be1912925b8c076e4d10b6893f85 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 07:29:36 +0000 Subject: [PATCH 41/66] can't deny location access with winforms api --- testbed/tests/hardware/test_location.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testbed/tests/hardware/test_location.py b/testbed/tests/hardware/test_location.py index d093b9ca7f..7e595034e8 100644 --- a/testbed/tests/hardware/test_location.py +++ b/testbed/tests/hardware/test_location.py @@ -38,6 +38,9 @@ async def test_grant_permission(app, location_probe): async def test_deny_permission(app, location_probe): """A user can deny permission to use location.""" + + skip_on_platforms("windows") + # Initiate the permission request. As permissions are not primed, # they will be denied. assert not await app.location.request_permission() @@ -90,6 +93,9 @@ async def test_grant_background_permission(app, location_probe): async def test_deny_background_permission(app, location_probe): """A user can deny background permission to use location.""" + + skip_on_platforms("windows") + # Foreground permissions haven't been approved, so requesting background permissions # will raise an error. with pytest.raises( From bd3717a677beb30680aa756a717c5b77019484b4 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 07:38:36 +0000 Subject: [PATCH 42/66] use different Start overload --- winforms/src/toga_winforms/hardware/location.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 68b0b9866d..1a2fee6851 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -31,8 +31,6 @@ def __init__(self, interface): self._handler = EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( self._position_changed ) - - self.watcher.Start() self._has_permission = False self._has_background_permission = False @@ -50,6 +48,7 @@ def has_background_permission(self): return self._has_background_permission def request_permission(self, future: AsyncResult[bool]) -> None: + self.watcher.Start(False) future.set_result(True) self._has_permission = True From 5fadf745aecb1485c4794eed5e22450e2359de99 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 07:41:23 +0000 Subject: [PATCH 43/66] use Permission property --- winforms/src/toga_winforms/hardware/location.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 1a2fee6851..e95dc53b62 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -6,6 +6,7 @@ GeoCoordinateWatcher, GeoPositionAccuracy, GeoPositionChangedEventArgs, + GeoPositionPermission, ) from toga import LatLng @@ -31,7 +32,6 @@ def __init__(self, interface): self._handler = EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( self._position_changed ) - self._has_permission = False self._has_background_permission = False def _position_changed( @@ -42,7 +42,7 @@ def _position_changed( self.interface.on_change(**location) def has_permission(self): - return self._has_permission + return self.watcher.Permission == GeoPositionPermission.Granted def has_background_permission(self): return self._has_background_permission @@ -50,7 +50,6 @@ def has_background_permission(self): def request_permission(self, future: AsyncResult[bool]) -> None: self.watcher.Start(False) future.set_result(True) - self._has_permission = True def request_background_permission(self, future: AsyncResult[bool]) -> None: if not self.has_permission(): From 4c4ca08d835d6b9a74ebb5060e96de181673df6f Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 07:44:10 +0000 Subject: [PATCH 44/66] set Permission property as required --- winforms/tests_backend/hardware/location.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 2f175ea751..3411746929 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -3,6 +3,7 @@ from pytest import xfail from System.Device.Location import ( GeoCoordinate, + GeoPositionPermission, ) from toga.types import LatLng @@ -28,13 +29,13 @@ def allow_permissions(self): pass def allow_permission(self): - pass + self.watcher.Permission = GeoPositionPermission.Granted def grant_permission(self): - self.app.location._impl._has_permission = True + self.watcher.Permission = GeoPositionPermission.Granted def reject_permission(self): - xfail("No support for permissions here") + self.watcher.Permission = GeoPositionPermission.Denied def add_location(self, location: LatLng, altitude, cached=False): m = Mock(spec=GeoCoordinate) From 8a29fc7f75d88b10d44ce3dec9eedd5a2534636b Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 07:44:50 +0000 Subject: [PATCH 45/66] remove dud method --- winforms/tests_backend/hardware/location.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 3411746929..7b658e7713 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -25,9 +25,6 @@ def cleanup(self): except AttributeError: pass - def allow_permissions(self): - pass - def allow_permission(self): self.watcher.Permission = GeoPositionPermission.Granted From 27beaa89e87379452b587f0bcfceb63b49ee7a08 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 07:46:55 +0000 Subject: [PATCH 46/66] add TODO --- winforms/src/toga_winforms/hardware/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index e95dc53b62..ed1eedafce 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -48,7 +48,7 @@ def has_background_permission(self): return self._has_background_permission def request_permission(self, future: AsyncResult[bool]) -> None: - self.watcher.Start(False) + self.watcher.Start(False) # TODO: where can we call stop? future.set_result(True) def request_background_permission(self, future: AsyncResult[bool]) -> None: From dceba831646c157c8fb28db042d758e9310ad96e Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 07:57:38 +0000 Subject: [PATCH 47/66] correct setting Permission --- winforms/tests_backend/hardware/location.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 7b658e7713..5b487fcad8 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -26,13 +26,13 @@ def cleanup(self): pass def allow_permission(self): - self.watcher.Permission = GeoPositionPermission.Granted + self.app.location._impl.watcher.Permission = GeoPositionPermission.Granted def grant_permission(self): - self.watcher.Permission = GeoPositionPermission.Granted + self.app.location._impl.watcher.Permission = GeoPositionPermission.Granted def reject_permission(self): - self.watcher.Permission = GeoPositionPermission.Denied + self.app.location._impl.watcher.Permission = GeoPositionPermission.Denied def add_location(self, location: LatLng, altitude, cached=False): m = Mock(spec=GeoCoordinate) From b69ac70a7786030c7af31502150209d158752b22 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 16:09:03 +0800 Subject: [PATCH 48/66] add spec --- winforms/tests_backend/hardware/location.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 5b487fcad8..9b1dd55e1b 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -3,6 +3,7 @@ from pytest import xfail from System.Device.Location import ( GeoCoordinate, + GeoCoordinateWatcher, GeoPositionPermission, ) @@ -14,7 +15,7 @@ class LocationProbe(HardwareProbe): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.app.location._impl.watcher = Mock() + self.app.location._impl.watcher = Mock(spec=GeoCoordinateWatcher) self.reset_locations() def cleanup(self): From 07e46fc30c4892fea580e4087a45f8e3f9d3fcc2 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 16:10:00 +0800 Subject: [PATCH 49/66] remove skips --- testbed/tests/hardware/test_location.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/testbed/tests/hardware/test_location.py b/testbed/tests/hardware/test_location.py index 7e595034e8..d093b9ca7f 100644 --- a/testbed/tests/hardware/test_location.py +++ b/testbed/tests/hardware/test_location.py @@ -38,9 +38,6 @@ async def test_grant_permission(app, location_probe): async def test_deny_permission(app, location_probe): """A user can deny permission to use location.""" - - skip_on_platforms("windows") - # Initiate the permission request. As permissions are not primed, # they will be denied. assert not await app.location.request_permission() @@ -93,9 +90,6 @@ async def test_grant_background_permission(app, location_probe): async def test_deny_background_permission(app, location_probe): """A user can deny background permission to use location.""" - - skip_on_platforms("windows") - # Foreground permissions haven't been approved, so requesting background permissions # will raise an error. with pytest.raises( From 8bb86b014ea5d2c381480820bd526f0430fdea4b Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 09:09:13 +0000 Subject: [PATCH 50/66] swap hardcoded True --- winforms/src/toga_winforms/hardware/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index ed1eedafce..ae8992401c 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -49,7 +49,7 @@ def has_background_permission(self): def request_permission(self, future: AsyncResult[bool]) -> None: self.watcher.Start(False) # TODO: where can we call stop? - future.set_result(True) + future.set_result(self.has_permission()) def request_background_permission(self, future: AsyncResult[bool]) -> None: if not self.has_permission(): From 0cdd98cd230c21192c230363567f333405f6ef42 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 09:23:24 +0000 Subject: [PATCH 51/66] restore skip --- testbed/tests/hardware/test_location.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testbed/tests/hardware/test_location.py b/testbed/tests/hardware/test_location.py index d093b9ca7f..467bcdb3c1 100644 --- a/testbed/tests/hardware/test_location.py +++ b/testbed/tests/hardware/test_location.py @@ -90,6 +90,8 @@ async def test_grant_background_permission(app, location_probe): async def test_deny_background_permission(app, location_probe): """A user can deny background permission to use location.""" + skip_on_platforms("windows") + # Foreground permissions haven't been approved, so requesting background permissions # will raise an error. with pytest.raises( From 4885cc8dac0e946ed67095a1b1a00e45b2d3a7c9 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Tue, 3 Dec 2024 17:49:32 +0800 Subject: [PATCH 52/66] fix data_file value --- testbed/tests/testbed.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/testbed/tests/testbed.py b/testbed/tests/testbed.py index 857eaaf05a..9ad60f971e 100644 --- a/testbed/tests/testbed.py +++ b/testbed/tests/testbed.py @@ -139,11 +139,15 @@ def run_tests(app, cov, args, report_coverage, run_slow, running_in_ci): "win32": "toga_winforms", }.get(sys.platform) + if "CI" in os.environ: + data_file = "D:\\a\\toga\\toga\\testbed\\logs\\coverage" + else: + data_file = None + # Start coverage tracking. # This needs to happen in the main thread, before the app has been created cov = coverage.Coverage( - # Don't store any coverage data - data_file=None, + data_file=data_file, branch=True, source_pkgs=[toga_backend], ) From e5332b3c590818812c26bd47d17c0786c5849ef4 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 4 Dec 2024 14:34:52 +0800 Subject: [PATCH 53/66] correct file name --- testbed/tests/testbed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbed/tests/testbed.py b/testbed/tests/testbed.py index 9ad60f971e..58d1160484 100644 --- a/testbed/tests/testbed.py +++ b/testbed/tests/testbed.py @@ -140,7 +140,7 @@ def run_tests(app, cov, args, report_coverage, run_slow, running_in_ci): }.get(sys.platform) if "CI" in os.environ: - data_file = "D:\\a\\toga\\toga\\testbed\\logs\\coverage" + data_file = "D:\\a\\toga\\toga\\testbed\\logs\\.coverage" else: data_file = None From 8fb7b8d92e4fa8ede9fb4eeb8e752eee3a485b3a Mon Sep 17 00:00:00 2001 From: Elliana May Date: Thu, 5 Dec 2024 09:50:59 +0800 Subject: [PATCH 54/66] use GITHUB_WORKSPACE --- testbed/tests/testbed.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testbed/tests/testbed.py b/testbed/tests/testbed.py index 58d1160484..89999879b2 100644 --- a/testbed/tests/testbed.py +++ b/testbed/tests/testbed.py @@ -140,7 +140,9 @@ def run_tests(app, cov, args, report_coverage, run_slow, running_in_ci): }.get(sys.platform) if "CI" in os.environ: - data_file = "D:\\a\\toga\\toga\\testbed\\logs\\.coverage" + data_file = ( + Path(os.environ["GITHUB_WORKSPACE"]) / "testbed" / "logs" / ".coverage" + ) else: data_file = None From 2f6f1daed9894d792f3f745a5104814cb553af4f Mon Sep 17 00:00:00 2001 From: Elliana May Date: Thu, 5 Dec 2024 09:54:14 +0800 Subject: [PATCH 55/66] include hidden files --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0f649ee59..9940854397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -403,6 +403,7 @@ jobs: with: name: testbed-failure-logs-${{ matrix.backend }} path: testbed/logs/* + include-hidden-files: true - name: Copy App Generated User Data if: failure() && matrix.backend != 'android' From 5deb049f86bb3cb03e66d19064fa8e1d15f045fa Mon Sep 17 00:00:00 2001 From: Elliana May Date: Thu, 5 Dec 2024 11:00:46 +0800 Subject: [PATCH 56/66] update docstring --- winforms/tests_backend/hardware/location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 9b1dd55e1b..877dfe2573 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -52,7 +52,7 @@ def reset_locations(self): def allow_background_permission(self): """ - we have access by default with winforms + winforms doesn't distinguish between foreground and background access """ pass From 9ca20911f0602af2bf6592a2333f852b56ae78e5 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 8 Dec 2024 10:56:31 +0800 Subject: [PATCH 57/66] handle None location --- winforms/src/toga_winforms/hardware/location.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index ae8992401c..b5a92b1ea4 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -58,7 +58,8 @@ def request_background_permission(self, future: AsyncResult[bool]) -> None: self._has_background_permission = True def current_location(self, result: AsyncResult[dict]) -> None: - result.set_result(toga_location(self.watcher.Position.Location)["location"]) + loco = toga_location(self.watcher.Position.Location) + result.set_result(loco["location"] if loco else None) def start_tracking(self) -> None: self.watcher.add_PositionChanged(self._handler) From fe39dfd2f801f480450157109271c5e470a56b35 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sat, 7 Dec 2024 22:55:45 +0800 Subject: [PATCH 58/66] ensure watcher starts, regardless of permissions state --- winforms/src/toga_winforms/hardware/location.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index b5a92b1ea4..1ce8a3388f 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -58,10 +58,12 @@ def request_background_permission(self, future: AsyncResult[bool]) -> None: self._has_background_permission = True def current_location(self, result: AsyncResult[dict]) -> None: + self.watcher.Start() # ensure watcher has started loco = toga_location(self.watcher.Position.Location) result.set_result(loco["location"] if loco else None) def start_tracking(self) -> None: + self.watcher.Start() # ensure watcher has started self.watcher.add_PositionChanged(self._handler) def stop_tracking(self) -> None: From 66a8081df45f51f46acc3e1548edcec4dd81bc6e Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 11 Dec 2024 08:07:59 +0000 Subject: [PATCH 59/66] add context manager for running the watcher --- .../src/toga_winforms/hardware/location.py | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 1ce8a3388f..614fcb3bc2 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -1,5 +1,7 @@ from __future__ import annotations +from contextlib import contextmanager + from System import EventHandler from System.Device.Location import ( GeoCoordinate, @@ -7,6 +9,7 @@ GeoPositionAccuracy, GeoPositionChangedEventArgs, GeoPositionPermission, + GeoPositionStatus, ) from toga import LatLng @@ -47,9 +50,19 @@ def has_permission(self): def has_background_permission(self): return self._has_background_permission + @contextmanager + def context(self): + already_started = self.watcher.Status == GeoPositionStatus.Ready + self.watcher.Start(False) + try: + yield + finally: + if not already_started: # don't want to stop if we're tracking + self.watcher.Stop() + def request_permission(self, future: AsyncResult[bool]) -> None: - self.watcher.Start(False) # TODO: where can we call stop? - future.set_result(self.has_permission()) + with self.context(): + future.set_result(self.has_permission()) def request_background_permission(self, future: AsyncResult[bool]) -> None: if not self.has_permission(): @@ -58,13 +71,15 @@ def request_background_permission(self, future: AsyncResult[bool]) -> None: self._has_background_permission = True def current_location(self, result: AsyncResult[dict]) -> None: - self.watcher.Start() # ensure watcher has started - loco = toga_location(self.watcher.Position.Location) - result.set_result(loco["location"] if loco else None) + with self.context(): + loco = toga_location(self.watcher.Position.Location) + # TODO: filter by horizontal accuracy? + result.set_result(loco["location"] if loco else None) def start_tracking(self) -> None: - self.watcher.Start() # ensure watcher has started + self.watcher.Start() self.watcher.add_PositionChanged(self._handler) def stop_tracking(self) -> None: + self.watcher.Stop() self.watcher.remove_PositionChanged(self._handler) From c4b1fc54b60f1d507538188a0d97eab57fa00d0f Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 11 Dec 2024 08:08:56 +0000 Subject: [PATCH 60/66] use boolean to track if tracking is on or off --- winforms/src/toga_winforms/hardware/location.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 614fcb3bc2..306af72cf4 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -9,7 +9,6 @@ GeoPositionAccuracy, GeoPositionChangedEventArgs, GeoPositionPermission, - GeoPositionStatus, ) from toga import LatLng @@ -35,6 +34,7 @@ def __init__(self, interface): self._handler = EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]]( self._position_changed ) + self._tracking = False self._has_background_permission = False def _position_changed( @@ -52,12 +52,12 @@ def has_background_permission(self): @contextmanager def context(self): - already_started = self.watcher.Status == GeoPositionStatus.Ready - self.watcher.Start(False) + if not self._tracking: + self.watcher.Start(False) try: yield finally: - if not already_started: # don't want to stop if we're tracking + if not self._tracking: # don't want to stop if we're tracking self.watcher.Stop() def request_permission(self, future: AsyncResult[bool]) -> None: @@ -79,7 +79,9 @@ def current_location(self, result: AsyncResult[dict]) -> None: def start_tracking(self) -> None: self.watcher.Start() self.watcher.add_PositionChanged(self._handler) + self._tracking = True def stop_tracking(self) -> None: self.watcher.Stop() self.watcher.remove_PositionChanged(self._handler) + self._tracking = False From 8b28b68b39deb49bd8243cc3319ad36793bca02c Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 11 Dec 2024 16:11:56 +0800 Subject: [PATCH 61/66] disable needs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9940854397..e6711c17dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -226,7 +226,7 @@ jobs: testbed: name: Testbed - needs: core + # needs: core runs-on: ${{ matrix.runs-on }} strategy: fail-fast: false From 5bad2e2b9bc513b382ab0f2de467cd6cd1fbf5ed Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 11 Dec 2024 08:37:11 +0000 Subject: [PATCH 62/66] add dummy event handler --- winforms/src/toga_winforms/hardware/location.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index 306af72cf4..d987a7ff73 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -36,6 +36,12 @@ def __init__(self, interface): ) self._tracking = False self._has_background_permission = False + self.watcher.OnPropertyChanged = self._property_changed + + def _property_changed(self, property_name: str): + if property_name == "Permission": + # TODO: handle permission changes + print("PERMISSION CHANGED", self.watcher.Permission) def _position_changed( self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] From 6fcac27978491ed5d33daf21ea9ea87214a84895 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 11 Dec 2024 08:37:35 +0000 Subject: [PATCH 63/66] add missing type --- winforms/src/toga_winforms/hardware/location.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index d987a7ff73..b29cd316a9 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -44,7 +44,9 @@ def _property_changed(self, property_name: str): print("PERMISSION CHANGED", self.watcher.Permission) def _position_changed( - self, sender, event: GeoPositionChangedEventArgs[GeoCoordinate] + self, + sender: GeoCoordinateWatcher, + event: GeoPositionChangedEventArgs[GeoCoordinate], ): location = toga_location(event.Position.Location) if location: From cefe8eb09d4441cdd027f419d40e4f7709287ddf Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 11 Dec 2024 19:44:01 +0800 Subject: [PATCH 64/66] rework current_location --- .../src/toga_winforms/hardware/location.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/winforms/src/toga_winforms/hardware/location.py b/winforms/src/toga_winforms/hardware/location.py index b29cd316a9..c851524d3c 100644 --- a/winforms/src/toga_winforms/hardware/location.py +++ b/winforms/src/toga_winforms/hardware/location.py @@ -79,10 +79,24 @@ def request_background_permission(self, future: AsyncResult[bool]) -> None: self._has_background_permission = True def current_location(self, result: AsyncResult[dict]) -> None: - with self.context(): - loco = toga_location(self.watcher.Position.Location) - # TODO: filter by horizontal accuracy? + def cb(sender, event): + if ( + event.Position.Location.IsUnknown + or event.Position.Location.HorizontalAccuracy > 100 + ): + return + self.watcher.remove_PositionChanged(cb) + loco = toga_location(event.Position.Location) result.set_result(loco["location"] if loco else None) + ctx.__exit__() + + ctx = self.context() + + ctx.__enter__() + + self.watcher.add_PositionChanged( + EventHandler[GeoPositionChangedEventArgs[GeoCoordinate]](cb) + ) def start_tracking(self) -> None: self.watcher.Start() From ad2380f5bd5c04bf6c93cf00e568a58673b433e8 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Fri, 20 Dec 2024 15:20:19 +0800 Subject: [PATCH 65/66] restore needs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6711c17dc..9940854397 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -226,7 +226,7 @@ jobs: testbed: name: Testbed - # needs: core + needs: core runs-on: ${{ matrix.runs-on }} strategy: fail-fast: false From 0714dcb66992b47644efe0027f5c13d9eba74b36 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Fri, 20 Dec 2024 16:08:26 +0800 Subject: [PATCH 66/66] Set supports_background_permission to False --- winforms/tests_backend/hardware/location.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/winforms/tests_backend/hardware/location.py b/winforms/tests_backend/hardware/location.py index 877dfe2573..8eccc6e8b6 100644 --- a/winforms/tests_backend/hardware/location.py +++ b/winforms/tests_backend/hardware/location.py @@ -13,6 +13,8 @@ class LocationProbe(HardwareProbe): + supports_background_permission = False + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.app.location._impl.watcher = Mock(spec=GeoCoordinateWatcher)