Repository: doronz88/pymobiledevice3 (language: python)

Issue:
Play GPX files IOS17
**Is your feature request related to a problem? Please describe.**
Related to issue #572. Currently no implementation of playing GPX files for IOS17 devices.

**Describe the solution you'd like**
Similar to the command used for releases before IOS17, a command which allows to play GPX files in IOS17

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**


Discussion / hints:

Fix patch:
diff --git a/pymobiledevice3/cli/developer.py b/pymobiledevice3/cli/developer.py
index ee1a529b7..71d4b86b7 100644
--- a/pymobiledevice3/cli/developer.py
+++ b/pymobiledevice3/cli/developer.py
@@ -739,9 +739,7 @@ def simulate_location_set(service_provider: LockdownClient, latitude, longitude)
 @click.argument('filename', type=click.Path(exists=True, file_okay=True, dir_okay=False))
 @click.option('--disable-sleep', is_flag=True, default=False)
 def simulate_location_play(service_provider: LockdownClient, filename, disable_sleep):
-    """
-    play a .gpx file
-    """
+    """ play a .gpx file """
     DtSimulateLocation(service_provider).play_gpx_file(filename, disable_sleep=disable_sleep)
 
 
@@ -963,7 +961,7 @@ def dvt_simulate_location():
 def dvt_simulate_location_clear(service_provider: LockdownClient):
     """ clear simulated location """
     with DvtSecureSocketProxyService(service_provider) as dvt:
-        LocationSimulation(dvt).stop()
+        LocationSimulation(dvt).clear()
 
 
 @dvt_simulate_location.command('set', cls=Command)
@@ -976,7 +974,17 @@ def dvt_simulate_location_set(service_provider: LockdownClient, latitude, longit
         ... set -- 40.690008 -74.045843 for liberty island
     """
     with DvtSecureSocketProxyService(service_provider) as dvt:
-        LocationSimulation(dvt).simulate_location(latitude, longitude)
+        LocationSimulation(dvt).set(latitude, longitude)
+        wait_return()
+
+
+@dvt_simulate_location.command('play', cls=Command)
+@click.argument('filename', type=click.Path(exists=True, file_okay=True, dir_okay=False))
+@click.option('--disable-sleep', is_flag=True, default=False)
+def dvt_simulate_location_play(service_provider: LockdownClient, filename: str, disable_sleep: bool) -> None:
+    """ play a .gpx file """
+    with DvtSecureSocketProxyService(service_provider) as dvt:
+        LocationSimulation(dvt).play_gpx_file(filename, disable_sleep=disable_sleep)
         wait_return()
 
 
diff --git a/pymobiledevice3/services/dvt/instruments/location_simulation.py b/pymobiledevice3/services/dvt/instruments/location_simulation.py
index 79c635e2c..d399768b0 100644
--- a/pymobiledevice3/services/dvt/instruments/location_simulation.py
+++ b/pymobiledevice3/services/dvt/instruments/location_simulation.py
@@ -1,18 +1,17 @@
-import logging
-
+from pymobiledevice3.services.dvt.instruments.location_simulation_base import LocationSimulationBase
 from pymobiledevice3.services.remote_server import MessageAux
 
 
-class LocationSimulation:
+class LocationSimulation(LocationSimulationBase):
     IDENTIFIER = 'com.apple.instruments.server.services.LocationSimulation'
 
     def __init__(self, dvt):
-        self.logger = logging.getLogger(__name__)
+        super().__init__()
         self._channel = dvt.make_channel(self.IDENTIFIER)
 
-    def simulate_location(self, latitude: float, longitude: float) -> None:
+    def set(self, latitude: float, longitude: float) -> None:
         self._channel.simulateLocationWithLatitude_longitude_(MessageAux().append_obj(latitude).append_obj(longitude))
         self._channel.receive_plist()
 
-    def stop(self) -> None:
+    def clear(self) -> None:
         self._channel.stopLocationSimulation()
diff --git a/pymobiledevice3/services/dvt/instruments/location_simulation_base.py b/pymobiledevice3/services/dvt/instruments/location_simulation_base.py
new file mode 100644
index 000000000..277b87973
--- /dev/null
+++ b/pymobiledevice3/services/dvt/instruments/location_simulation_base.py
@@ -0,0 +1,36 @@
+import logging
+import time
+from abc import abstractmethod
+
+import gpxpy
+
+
+class LocationSimulationBase:
+    def __init__(self):
+        self.logger = logging.getLogger(self.__class__.__name__)
+
+    @abstractmethod
+    def set(self, latitude: float, longitude: float) -> None:
+        pass
+
+    @abstractmethod
+    def clear(self) -> None:
+        pass
+
+    def play_gpx_file(self, filename: str, disable_sleep: bool = False):
+        with open(filename) as f:
+            gpx = gpxpy.parse(f)
+
+        last_time = None
+        for track in gpx.tracks:
+            for segment in track.segments:
+                for point in segment.points:
+                    if last_time is not None:
+                        duration = (point.time - last_time).total_seconds()
+                        if duration >= 0:
+                            if not disable_sleep:
+                                self.logger.info(f'waiting for {duration}s')
+                                time.sleep(duration)
+                    last_time = point.time
+                    self.logger.info(f'set location to {point.latitude} {point.longitude}')
+                    self.set(point.latitude, point.longitude)
diff --git a/pymobiledevice3/services/remote_server.py b/pymobiledevice3/services/remote_server.py
index 78d37d8ee..d22f9f08f 100644
--- a/pymobiledevice3/services/remote_server.py
+++ b/pymobiledevice3/services/remote_server.py
@@ -391,7 +391,7 @@ def __enter__(self):
         return self
 
     def __exit__(self, exc_type, exc_val, exc_tb):
-        self.channel.stop(expects_reply=False)
+        self.channel.clear(expects_reply=False)
 
     def __iter__(self):
         while True:
diff --git a/pymobiledevice3/services/simulate_location.py b/pymobiledevice3/services/simulate_location.py
index 4cc409699..283887ad1 100755
--- a/pymobiledevice3/services/simulate_location.py
+++ b/pymobiledevice3/services/simulate_location.py
@@ -1,24 +1,23 @@
 import struct
-import time
-
-import gpxpy
 
 from pymobiledevice3.lockdown import LockdownClient
+from pymobiledevice3.services.dvt.instruments.location_simulation_base import LocationSimulationBase
 from pymobiledevice3.services.lockdown_service import LockdownService
 
 
-class DtSimulateLocation(LockdownService):
+class DtSimulateLocation(LockdownService, LocationSimulationBase):
     SERVICE_NAME = 'com.apple.dt.simulatelocation'
 
     def __init__(self, lockdown: LockdownClient):
-        super().__init__(lockdown, self.SERVICE_NAME)
+        LockdownService.__init__(self, lockdown, self.SERVICE_NAME)
+        LocationSimulationBase.__init__(self)
 
-    def clear(self):
+    def clear(self) -> None:
         """ stop simulation """
         service = self.lockdown.start_lockdown_developer_service(self.SERVICE_NAME)
         service.sendall(struct.pack('>I', 1))
 
-    def set(self, latitude: float, longitude: float):
+    def set(self, latitude: float, longitude: float) -> None:
         """ stop simulation """
         service = self.lockdown.start_lockdown_developer_service(self.SERVICE_NAME)
         service.sendall(struct.pack('>I', 0))
@@ -26,21 +25,3 @@ def set(self, latitude: float, longitude: float):
         longitude = str(longitude).encode()
         service.sendall(struct.pack('>I', len(latitude)) + latitude)
         service.sendall(struct.pack('>I', len(longitude)) + longitude)
-
-    def play_gpx_file(self, filename: str, disable_sleep: bool = False):
-        with open(filename) as f:
-            gpx = gpxpy.parse(f)
-
-        last_time = None
-        for track in gpx.tracks:
-            for segment in track.segments:
-                for point in segment.points:
-                    if last_time is not None:
-                        duration = (point.time - last_time).total_seconds()
-                        if duration >= 0:
-                            if not disable_sleep:
-                                self.logger.info(f'waiting for {duration}s')
-                                time.sleep(duration)
-                    last_time = point.time
-                    self.logger.info(f'set location to {point.latitude} {point.longitude}')
-                    self.set(point.latitude, point.longitude)
diff --git a/pymobiledevice3/services/web_protocol/cdp_target.py b/pymobiledevice3/services/web_protocol/cdp_target.py
index 88da294dc..007395ac9 100644
--- a/pymobiledevice3/services/web_protocol/cdp_target.py
+++ b/pymobiledevice3/services/web_protocol/cdp_target.py
@@ -356,7 +356,7 @@ async def _page_start_screencast(self, message):
 
     async def _page_stop_screencast(self, message):
         if self.screencast is not None:
-            await self.screencast.stop()
+            await self.screencast.clear()
             self.screencast = None
         await self._simple_response(message, None)
 


Test patch:
diff --git a/tests/services/instruments/test_location.py b/tests/services/instruments/test_location.py
index f39533228..25a0bccd5 100644
--- a/tests/services/instruments/test_location.py
+++ b/tests/services/instruments/test_location.py
@@ -6,7 +6,7 @@ def test_set_location(dvt):
     Test set location.
     """
     # set to liberty island
-    LocationSimulation(dvt).simulate_location(40.690008, -74.045843)
+    LocationSimulation(dvt).set(40.690008, -74.045843)
 
 
 def test_clear_location(dvt):
@@ -14,4 +14,4 @@ def test_clear_location(dvt):
     Test clear location simulation
     """
     # set to liberty island
-    LocationSimulation(dvt).stop()
+    LocationSimulation(dvt).clear()