Упражнение 2. Приложение Running Tracker

Пока что наше приложение не использует службы определения местоположения, чтобы прокладывать маршрут из одной точки в другую. Вместо этого оно обращается к статистическому набору координат начальной и конечной точек маршрута, жестко заданных в коде C#. В этом задании мы добавим в приложение поддержку определения местоположения.

Чтобы добавить в Running Tracker эту функцию и отображать соответствующие данные на карте, нужно изменить манифест приложения. Наконец, мы добавим в приложение поддержку фонового режима.

Примечание.

В этом упражнении мы будем использовать код, созданный нами в предыдущем упражнении, и поэтапно вносить в него изменения. Если вы пропустили предыдущее упражнение и начинаете работу с данного практического занятия, используйте решение Begin. Это решение расположено в установочной папке Sources\EX2 – Location Services\Begin.

Задание 1. Изменение манифеста приложения

Задача приложения — отслеживать местоположение пользователя во время движения. Это означает, что приложение должно оставаться активным в течение всей пробежки. Все это время телефон использоваться не будет либо пользователь откроет другое приложение. По умолчанию при запуске другой программы наше приложение деактивируется и прекратит отслеживать маршрут, время и дистанцию, пройденную пользователем. Чтобы приложение отслеживало местоположение пользователя в фоновом режиме, следует изменить манифест приложения.

  1. В обозревателе решений перейдите в папку Properties и найдите файл WMAppManifest.xml. Дважды щелкните этот файл и откройте конструктор манифеста.
  2. Перейдите в раздел Capabilities (Способности), найдите функцию IC_CAP_LOCATION и включите ее, установив флажок.

    Рис. 4.
    Раздел Capabilities в манифесте приложения

  3. Теперь мы добавим функцию фонового выполнения приложения. Конструктор манифеста не поддерживает данную часть манифеста приложения; мы будем вносить изменения вручную. Закройте конструктор приложения и откройте файл манифеста в XML-редакторе: правой кнопкой щелкните файл WMAppManifest.xml и выберите команду Open With… (Открыть с помощью...) в контекстном меню.

    Рис. 5.
    Команда Open With в контекстном меню файла

  4. Выберите из диалогового окна Open With пункт XML (Text) Editor и нажмите кнопку OK.

    Рис. 6.
    Диалоговое окно выбора XML-редактора

  5. Теперь манифест открыт в XML-редакторе. Перейдите в раздел Tasks файла XML и найдите в нем элемент DefaultTask.
  6. Замените элемент DefaultTask следующим кодом:
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" > <BackgroundExecution> <ExecutionType Name="LocationTracking" /> </BackgroundExecution> </DefaultTask>

Теперь приложение будет оставаться активным в фоновом режиме и отслеживать местоположение пользователя. Приложение для отслеживания местоположения работает в Windows Phone 8 в фоновом режиме, даже если пользователь запустил другое приложение.

7. Теперь приложение готово к внесению следующих изменений.

RunningInBackground="Application_RunningInBackground"

Задание 2. Использование служб определения местоположения и управление фоновым режимом выполнения

Начиная с этого момента приложение будет использовать реальную информацию о местоположении вместо эмулированных статических координат, которые мы создали в предыдущем задании. Мы изменим несколько методов, а также модифицируем пользовательский интерфейс, чтобы на экране отображалась информация о движении пользователя.

  1. После добавления функции фонового выполнения мы внесем дополнительные изменения для поддержки данного режима. Найдите и откройте файл App.xaml. Найдите элемент shell:PhoneApplicationService и добавьте следующий код сразу же перед закрывающим тэгом «/>»:
RunningInBackground="Application_RunningInBackground"

2. Данный код регистрирует обработчик событий для события RunningInBackground, который следует добавить в файл кода программной части. Откройте файл App.xaml.cs и добавьте в класс следующий метод обработчика событий:

private void Application_RunningInBackground (object sender, RunningInBackgroundEventArgs e) { IsInBackground = true; }

3. Добавьте в класс следующее статическое поле:

public static bool IsInBackground = false;

4. Найдите и откройте файл MainPage.xaml.

5. Найдите элемент ContentPanel Grid и добавьте следующий код сразу же перед закрывающим тэгом “</Grid>” :

<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Stretch" x:Name="stkWorkoutDetails"> <TextBlock Text="Workout details: "/> <TextBlock Text="{Binding Mileage, StringFormat=\{0:F\} mi.}"/> <TextBlock Text=" in "/> <TextBlock Text="{Binding Runtime, StringFormat=\{0:hh\\:mm\\:ss\} minutes}"/> </StackPanel>

Примечание.

Этот код определяет привязку данных в полях Mileage (Расстояние) и Runtime (Время пробежки), которые мы создадим далее. Чтобы получить больше информации о привязке данных, см. документацию по Windows Phone.

6. Найдите и откройте файл MainPage.xaml.cs.

7. В самом начале класса добавьте следующие выражения using:

using System.Windows.Media; using System.Diagnostics;

8. Удалите определенные в классе константы: SourceLatitude, SourceLongtitude, DestinationLatitude, DestinationLongtitude.

9. Добавьте следующие поля вместо удаленных:

private const float KM_MILE = 0.000621371192f; GeoCoordinateWatcher gcw; bool isRunning = false; int count = 0; MapLayer historicalReadingsLayer = new MapLayer(); MapOverlay overlay = new MapOverlay(); MapPolyline polyline = new MapPolyline(); TextBlock txtCurrentSpeed = new TextBlock(); List<GeoPosition<GeoCoordinate>> positions = new List<GeoPosition<GeoCoordinate>>();

Константа KM_MILE служит для пересчета расстояния из метров (которые возвращает класс RouteQuery) в мили.

GeoCoordinateWatcher (из пространства имен System.Device.Location) реализует службу определения местоположения Windows Phone 8. Она будет следит за движением пользователя.

MapLayer, MapOverlay и MapPolyline — это классы из пространства имен Microsoft.Phone.Maps.Controls, которые помогут указывать маршрут пробежки на карте.

Класс MapLayer представляет уровень, указанный в верхней части элемента управления картой, где отображается коллекция объектов MapOverlay.

Класс MapOverlay содержит коллекцию элементов пользовательского интерфейса UIElements и помогает позиционировать их с помощью географических координат.

Класс MapPolyline — это ломаная линия (она состоит из нескольких прямых сегментов) на элементе управления картой, отображающая маршрут пользователя.

Наконец, список positions служит для сбора данных о местоположении, полученных во время пробежки, и расчета расстояния, которое пользователь пробежал на данный момент.

10. В конце класса MainPage добавьте следующий код:

public float Mileage { get { return (float)GetValue(MileageProperty); } set { SetValue(MileageProperty, value); } } public static readonly DependencyProperty MileageProperty = DependencyProperty.Register ("Mileage", typeof(float), typeof(MainPage), new PropertyMetadata(0.0f)); public TimeSpan Runtime { get { return (TimeSpan)GetValue(RuntimeProperty); } set { SetValue(RuntimeProperty, value); } } public static readonly DependencyProperty RuntimeProperty = DependencyProperty.Register("Runtime", typeof(TimeSpan), typeof(MainPage), new PropertyMetadata(TimeSpan.Zero));

Данный код добавляет два свойства зависимостей, которые позволят отображать движение пользователя по маршруту (пройденное им расстояние).

11. Чтобы обеспечить привязку данных к экранным элементам и свойствам, добавленным в предыдущих фрагментах кода, откройте конструктор класса и добавьте следующую строку после вызова метода InitializeComponent:

stkWorkoutDetails.DataContext = this;

12. Найдите метод OnNavigatedTo. В начале метода инициализации GeoCoordinateWatcher добавьте следующий код:

gcw = new GeoCoordinateWatcher(GeoPositionAccuracy.High); gcw.PositionChanged += gcw_PositionChanged; gcw.StatusChanged += gcw_StatusChanged;

Этот код создает новый экземпляр класса GeoCoordinateWatcher и требует высокой точности результатов, необходимых для отслеживания местоположения пользователя. Кроме того, данный код осуществляет подписку на события PositionChanged и StatusChanged.

13. Далее в методе OnNavigatedTo найдите строки, которые меняют routeQuery.TravelMode и осуществляют подписку на событие routeQuery.QueryCompleted, и замените их следующим кодом:
if (!App.IsInBackground) { txtCurrentSpeed.Foreground = new SolidColorBrush(Colors.Black); overlay.Content = txtCurrentSpeed; routeQuery.TravelMode = Microsoft.Phone.Maps.Services. TravelMode.Walking; routeQuery.QueryCompleted += rq_QueryCompleted; MapLayer currentSpeedLayer = new MapLayer(); currentSpeedLayer.Add(overlay); map.Layers.Add(currentSpeedLayer); map.Layers.Add(historicalReadingsLayer); map.MapElements.Add(polyline); }

В первую очередь этот код отвечает за то, что фоновый режим приложения не включается вплоть до выполнения инициализации.

Примечание.

Когда приложение для отслеживания местоположения работает в фоновом режиме, оно остается активным, то есть при выходе из фонового режима не требует повторной инициализации.

Переменная txtCurrentSpeed — это элемент TextBlock, который динамически добавляется в элемент управления картой и отображает текущую скорость.

После инициализации объекта RouteQuery код инициализирует новый объект MapLayer, который используется для отображения маршрута, скорости и пройденных точек в элементе управления картой.

14. Далее добавьте код, реализующий обработчик событий StatusChanged для класса GeoCoordinateWatcher:
void gcw_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) { Debug.WriteLine("New status: " + e.Status); if (e.Status == GeoPositionStatus.Ready) { map.Center = gcw.Position.Location; polyline.Path.Clear(); btnStartStop.Content = "Stop"; map.Visibility = System.Windows.Visibility.Visible; Mileage = 0; Runtime = TimeSpan.Zero; } }

Данный фрагмент кода — это консоль вывода данных Debug с новым состоянием местоположения (мы будем использовать ее в задании 3). Когда службы определения местоположения будут готовы, консоль центрирует карту в соответствии с текущим местоположением. Затем данный метод готовит карту, переменные и элементы пользовательского интерфейса для новой пробежки.

15. Далее добавьте код, реализующий обработчик событий PositionChanged для класса GeoCoordinateWatcher:
void gcw_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) { Debug.WriteLine("New position:" + e.Position.Location.Latitude + "," + e.Position.Location.Longitude); map.Center = e.Position.Location; map.Heading = e.Position.Location.Course; count++; polyline.Path.Add(e.Position.Location); if (count > 1) { txtCurrentSpeed.Text = e.Position.Location.Speed + "m/s"; overlay.GeoCoordinate = e.Position.Location; } if (positions.Count > 0) { MapOverlay ovrl = new MapOverlay(); TextBlock txtTimestamp = new TextBlock(); txtTimestamp.Foreground = new SolidColorBrush(Colors.Black); ovrl.Content = txtTimestamp; ovrl.GeoCoordinate = positions.Last().Location; txtTimestamp.Text = string.Format("{0:hh\\:mm\\:ss}", positions.Last().Timestamp.TimeOfDay); historicalReadingsLayer.Add(ovrl); } positions.Add(e.Position); waypoints.Add(e.Position.Location); if (!routeQuery.IsBusy && count > 1) { routeQuery.InitialHeadingInDegrees = e.Position.Location.Course; routeQuery.Waypoints = waypoints; routeQuery.QueryAsync(); } }

Этот метод вызывается каждый раз, когда службы определения местоположения предоставляют новые сведения о местоположении. Он отвечает за центрирование карты в текущем местоположении пользователя, расчет и отображение текущего времени и скорости, а также отрисовку маршрута на карте.

Реализованный в предыдущем задании последний сегмент вызывает асинхронный метод QueryAsync в члене routeQuery с новой промежуточной точкой только в том случае, если RouteQuery не занят обработкой предыдущего запроса.

16. Далее измените обработчик события QueryCompleted, созданный в предыдущем упражнении. Найдите метод rq_QueryCompleted и замените его следующим кодом:

TimeSpan timeDiff = positions.Last().Timestamp. TimeOfDay - positions.First().Timestamp.TimeOfDay; Mileage = float.Parse(e.Result.LengthInMeters.ToString()) * KM_MILE; Runtime = timeDiff; Debug.WriteLine("Passed: " + Mileage + " miles. in " + string.Format("{0:hh\\:mm\\:ss}", Runtime));

Теперь метод рассчитывает общее время пробежки и расстояние и присваивает его свойствам, объявленным в начале данного задания.

Примечание.

Свойства Mileage и Runtime привязаны к текстовым блокам экрана, благодаря чему информация на экране обновляется автоматически каждый раз, когда эти свойства присваиваются кодом.

17. В предыдущем задании использовалась только кнопка Start, которая запускала сессию во время пробежки. Теперь мы внедрим полный механизм Start/Stop в том виде, в котором он был спроектирован изначально. Чтобы это сделать, найдите метод обработчика событий btnStartStop_Click и замените его следующим кодом:

if (!isRunning) { map.Visibility = Visibility.Collapsed; txtMessage.Text = "Getting ready..."; gcw.Start(); polyline.Path.Clear(); isRunning = true; } else { gcw.Stop(); btnStartStop.Content = "Start"; isRunning = false; }

Данный код запускает метод GeoCoordinateWatcher, если пользователь стоит, либо останавливает код, если в настоящее время приложение отслеживает местоположение (т. е. пользователь бежит).

18. Теперь можно скомпилировать и запустить приложение. Чтобы протестировать его, перейдите к следующему заданию.

Задание 3. Тестирование приложения

Чтобы протестировать приложение, нам потребуется эмулировать изменения местоположения, которые сообщает служба определения местоположения. Мы можем использовать физическое устройство под управлением Windows Phone 8, но проще применить эмулятор Windows Phone 8. Эмулятор моделирует движение пользователя и поможет нам протестировать службы определения местоположения.

  1. Скомпилируйте и разверните приложение на эмуляторе Windows Phone. Запустите его.
  2. В верхнем правом углу на главном экране эмулятора находится строка меню. Щелкните кнопку «>>», чтобы открыть дополнительную панель инструментов.

    Рис. 7.
    Открытие дополнительных инструментов из эмулятора

  3. Перейдите на вкладку Location (Местоположение).

    Рис. 8.
    Открытие дополнительных инструментов в эмуляторе

    Как видно на рисунке, вкладка Location отображает карту, которую можно использовать для эмуляции местоположения устройства (т. е. имитировать движение из одной точки в другую). Чтобы эмулировать движение, выполняйте щелчки по карте, и она будет передавать в эмулятор GPS-данные об изменении местоположения. Можно также записать эти данные и воспроизвести их в эмуляторе. Записав маршрут и запустив его на воспроизведение, удобно тестировать набор известных маршрутов без необходимости постоянно щелкать карту. Кроме того, можно управлять частотой, с которой эмулированные события движения передаются в эмулятор.

  4. Вернемся к приложению Running Tracker. Коснитесь кнопки Start, а затем начните щелкать карту эмулятора, чтобы смоделировать маршрут. Голубая линия представляет собой маршрут, отображаемый в приложении.

    Рис. 9.
    Моделирование движения в эмуляторе Windows Phone

  5. Чтобы протестировать работу приложения в фоновом режиме, нажмите кнопку Home (Главная), которая деактивирует приложение. Щелкните карту эмулятора еще несколько раз, чтобы передать в эмулятор данные о новых перемещениях.

    Рис. 10.
    Эмуляция событий в приложении, работающем в фоновом режиме

    Если приложение работает с подключенным отладчиком, перейдите в окно инструмента Output (Вывод) в Visual Studio 2012 и понаблюдайте за данными отладчика, которые появляются даже в том случае, если приложение работает в фоне.

    Рис. 11.
    Вывод данных отладчика в Visual Studio

    Это означает, что приложение получает события от службы определения местоположения и корректно их обрабатывает.

  6. Нажмите на кнопку Back (Назад) в эмуляторе устройства, чтобы повторно активировать приложение и проверить весь маршрут.

    Рис. 12.
    Обновление маршрута после повторной активации приложения

  7. На этом наше практическое занятие завершено.

Предыдущая | Назад