Un finto GPS per agevolare lo sviluppo in Windows Phone

di Cristian Civera, in Windows Phone,

Per lo sviluppo per Windows Phone, Visual Studio 2010 e l'emulatore sono due ottimi strumenti che permettono un facile debugging e testing delle applicazioni. La maggior parte delle caratteristiche sono simulate, come effettuare chiamate oppure ruotare il dispositivo, ma non è possibile testare il GPS incorporato nel dispositivo.

Questo in realtà non è un problema, perché la soluzione più rapida è sicuramente provare sul dispositivo fisico. Questa pratica però è scomoda e non permette di simulare situazioni o avere dati con un significato valido, perciò è utile poter sviluppare un finto GPS che permetta di testare l'applicazione creando una situazione verosimile.

Tutto questo è possibile grazie all'interfaccia IGeoPositionWatcher implementata dalla classe GeoCoordinateWatcher la quale lavora sul dispositivo fisico, ma è possibile implementare una versione alternativa che fornisca informazioni fittizie, pur mantenendo la stessa modalità di utilizzo. Ipotizzando di creare una classe di nome FakeGeoPositionWatcher, è possibile scegliere la tipologia di classe da implementare a seconda che l'applicativo stia girando all'interno dell'emulatore o sul dispositivo reale.

IGeoPositionWatcher<GeoCoordinate> watcher;
if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)
{
  watcher = new GeoCoordinateWatcher();
}
else
{
  watcher = new FakeGeoPositionWatcher();
}

// Intercetto gli eventi e avvio
watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
watcher.Start();

L'implementazione di FakeGeoPositionWatcher non fa altro che supportare gli eventi StatusChanged e PositionChanged, e scatenare ad intervalli regolari il cambio di posizione simulando lo spostamento del segnale GPS. Di seguito l'implementazione completa:

public class FakeGeoPositionWatcher : IGeoPositionWatcher<GeoCoordinate>
{

  private AsyncOperation operation;
  private Timer timer;

  // Posizione iniziale
  private GeoPosition<GeoCoordinate> position = new GeoPosition<GeoCoordinate>(DateTimeOffset.Now, new GeoCoordinate(45.477827, 9.310956, 0, 30, 30, 0, 0));

  public GeoPosition<GeoCoordinate> Position
  {
    get
    {
      return position;
    }
  }

  public GeoPositionStatus Status
  {
    get;
    set;
  }

  public event EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>> PositionChanged;

  public event EventHandler<GeoPositionStatusChangedEventArgs> StatusChanged;

  public void Start(bool suppressPermissionPrompt)
  {
    if (timer == null)
    {
      operation = AsyncOperationManager.CreateOperation(null);

      // Timer per scatenare il cambio di posizione
      timer = new Timer(Raise, null, 0, 5000);

      // Scateno lo startup
      if (StatusChanged != null)
        StatusChanged(this, new GeoPositionStatusChangedEventArgs(GeoPositionStatus.Initializing));
      if (StatusChanged != null)
        StatusChanged(this, new GeoPositionStatusChangedEventArgs(GeoPositionStatus.Ready));
    }
  }

  public void Start()
  {
    this.Start(false);
  }

  public void Stop()
  {
    if (timer != null)
      timer.Dispose();
    timer = null;
  }

  public bool TryStart(bool suppressPermissionPrompt, TimeSpan timeout)
  {
    this.Start();
    return true;
  }

  private void Raise(object s)
  {
    operation.Post(st =>
    {
      this.Position.Location = new GeoCoordinate(this.Position.Location.Latitude,
        this.Position.Location.Longitude + 0.0002,
        this.Position.Location.Altitude + 0.0002,
        Math.Max(2, this.Position.Location.HorizontalAccuracy - 5),
        Math.Max(2, this.Position.Location.VerticalAccuracy - 5),
        this.Position.Location.Speed,
        this.Position.Location.Course);

      if (PositionChanged != null)
        PositionChanged(this, new GeoPositionChangedEventArgs<GeoCoordinate>(this.Position));
    }, null);
  }
}

Da notare che gli eventi, per mantenere la stessa caratteristica dell'implementazione reale, devono essere scatenati sul thread UI. Per farlo si fa uso della classe AsyncOperationManager di cui è possibile trovare un approfondimento all'indirizzo
https://www.silverlightitalia.com/articoli/silverlight/multithreading-silverlight-2.0.aspx

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi