UAP - 使用 Geofencing 地理圍欄

UAP - 使用 Geofencing 地理圍欄

Geofencing, start to finish (XAML) 這是 WP 8.1 時提出的功能,我一直對它感到好奇,因為可應用的層面太廣了,

例如:定點廣告/促銷、觀光區資訊提示、維修人員路程、客戶拜訪通知…等。所以這一篇將來好好了解它。

 

Geofencing 允許 App 定義 geographical region (地理範圍) 與 App 接收系統通知,讓 設備 進入該範圍或存在附近時可以啟動

“Geofencing allows an app to define a geographical region and have the system alert the app when the device it's running on enters or exits that area. ”

這正是 Geofencing 的特性與重點。藉由這樣的功能可以在設備進入、範圍內或是離開時結合系統的提醒、推播或是相關功能,

如果正好開啟 App 可以跳出 dialog 的方式讓用戶得知相關的資訊。

要達成上述的目標有幾個步驟要處理:

  • 選擇要定義 Geofence 的地點,用來讓 App 知道有那些地理範圍是目的地;
  • App 需要註冊與處理:
    • 宣告與取得設備的 location 資訊,並且要定時或固定距離更新座標。
    • 註冊 event handler,分別在 foreground / background task 的處理是不同的處理方式。

 

以下便針對這幾個步驟來加以說明,詳細的內容可以參考<Guidelines for geofencing>與<Set up a geofence>。

 

A. 操作 Geofecing 類別,注冊指定的 geographical region

GeofenceMonitor

     包含所有注冊的 Geofence objects資訊,并且提供狀態事件提供程式可以加以監控并取得指定的 geofence。重點如下:

Type Name Description
Event GeofenceStateChanged Raised when the state of one or more Geofence objects in the Geofences collection of the GeofenceMonitor has changed.
該事件被觸發來自:
1. 當 App 修改了相關 geofence 物件的設定。
2. 當 App 向 GeofenceMonitor 請求讀取 ReadReports  中的内容。
     允許 App 從 suspended 返回時任然可以取得未讀取 queue 中的報告。
     相同的在 Background Task 也可以讀取,如果得到的是 SystemCondiction 代表目前設備沒有網路或是不被允許使用座標資訊。
  StatusChanged Raised when the status of the GeofenceMonitor has changed.
Method ReadReports Gets a collection of status changes to the Geofence objects in the Geofences collection of the GeofenceMonitor.
Property Current Read-only. Gets the GeofenceMonitor object which contains all of an app's Geofence information.
  Geofences Read-only. Returns a vector of the app's Geofence objects currently registered with the system wide GeofenceMonitor.
  LastKnownGeoposition Read-only. Last reading of the device's location.
  Status Read-only.  Indicates the current state of the GeofenceMonitor.
Member Value Description
Ready 0 The monitor is ready and active.
Initializing 1 The monitor is in the process of initializing.
NoData 2 There is no data on the status of the monitor.
Disabled 3 Access to location is denied.
NotInitialized 4 The geofence monitor has not been initialized.
NotAvailable 5 The geofence monitor is not available.

 

 

Geofence

    作用于定義 geofence 資訊,範圍來協助監控。

Type Name Description
Properties Duration Read-only. Gets the time window, beginning after the StartTime, during which the Geofence is monitored.
  DwellTime Read-only. The minimum time that a position has to be inside or outside of the Geofence in order for the notification to be triggered.
  Geoshape Read-only. The shape of the geofence region.
  Id Read-only. The id of the Geofence.
  MonitoredStates Read-only. Indicates the states that the Geofence is being monitored for.
MonitoredGeofenceStates
Member Description
None | none 0, No flag is set.
Entered | entered 1, The device has entered a geofence area.
Exited | exited 2, The device has left a geofence area.
Removed | removed 4, The geofence has been removed.
在建立 geofence 必須指定 Entered 與 Exited 兩個狀態的事件,不可以在建立 geofence 時使用 Removed。
  SingleUse Read-only. Indicates whether the Geofence should be triggered once or multiple times.
The default value for SingleUse is false.
If a geofence is being monitored for both Entered and Exited events and SingleUse is set to true, then the geofence will be removed after the user has both entered and exited the geofence.
  StartTime Read-only. The time to start monitoring the Geofence.
The default value for this property is a DateTime value of 0, which is the beginning of time, epoch.
當時間通過 StartTime 時啓動監控,當設備進入指定的 geofence 時狀態會變爲 Entered,此時 geofence 會開始計算停留時間(DwellTime),如果滿足了就會觸發事件。
如果 geofence 被啓動時設備在它之外, geofence 不應該被變成 Exited 狀態,設備應該要先進入 geofence 在指定的停留時間再離開才可以完成 Exited 的狀態。
Constructor Geofence(String,IGeoshape,MonitoredGeofenceStates,
Boolean, TimeSpan,DateTime,TimeSpan)
Initializes a new Geofence object given the id, the shape of the geofence, the states to monitor the geofence for, the singleUse flag, the dwellTime for the geofence, the time to start monitoring the geofence, and the duration of the geofence.

IGeoshape
定義 geographic shape 的界面,主要屬性如下:
Property Access type Description
AltitudeReferenceSystem Read-only The altitude reference system (高度參考系統) of the geographic shape. member:
Member Description
Unspecified 0, The altitude reference system was not specified.
Terrain 1, The altitude reference system is based on distance above terrain or ground level.
Ellipsoid 2, The altitude reference system is based on an ellipsoid which is a mathematical approximation of the shape of the Earth.
Geoid 3, The altitude reference system is based on the distance above sea level.
Surface 4, The altitude reference system is based on the distance above the tallest surface structures, such as buildings, trees, roads, etc., above terrain or ground level.
Note that the Terrain, Geoid, and Surface are implementation dependent and not mathematically precise.
GeoshapeType Read-only

The type of geographic shape. member:

Member Description
Geopoint 0, The geographic region is a point.
Geocircle 1, The geographic region is a circle with a center point and a radius.
Geopath 2, The geographic region is an order series of points.
GeoboundingBox 3, The geographic region is a rectangular region. (地理邊界框)
SpatialReferenceId Read-only The spatial reference identifier for the geographic shape, corresponding to a spatial reference system based on the specific ellipsoid used for either flat-earth mapping or round-earth mapping.
The default SRID on Windows and Windows Phone is 4326 which is WGS84 ellipsoid.

坐標精準度的比較如下:

  • GPS : within approximately 10 meters
  • Wi-Fi : between approximately 30 meters and 500 meters
  • Cell towers : between approximately 300 meters and 3,000 meters
  • IP address : between approximately 1,000 meters and 5,000 meters

更多詳細的説明可以參考《Windows.Devices.Geolocation namespace》,《obtaining the device's current geographic location》,

tracking the device's location over time》。

 

 

 

B. 如何取得 Device 的座標與更新它

     參考<Detect the user's location>的說明,先在 package.manifest 宣告需要的 capability 接著使用 RequestAccessAsync 向用戶請求開啟定位權限。

image

   1: <Capabilities>
   2:     <Capability Name="internetClientServer" />
   3:     <DeviceCapability Name="location" />
   4: </Capabilities>

過去在 WP 7 時候也有寫過相關的文件:<Windows Phone - 深入使用Bing Map - 客製Pushpin>、<Windows Phone 7 - 使用Google Map API將地址轉成座標>。

 

Geolocator class

     負責連接與存取 current geographic location。重要元素如下:

Type Name Description
Event PositionChanged Raised when the location is updated.
PositionChangedEventArgs 為重要參數,其中 Position 屬性代表座標改變後的值。
  StatusChanged Raised when the ability of the Geolocator to provide updated location changes.
StatusChangedEventArgs.Status 為重要參數,其列舉值:
Member Value Description
Ready 0 Location data is available.
Initializing 1 Location services is initializing. This is the status if a GPS is the source of location data and the GPS receiver does not yet have the required number of satellites in view to obtain an accurate position.
NoData 2 No location data is available from any source.
LocationStatus will have this value if the application calls GetGeopositionAsync or registers an event handler for the PositionChanged event, before data is available from a location sensor.
Once data is available LocationStatus transitions to the Ready state.
Disabled 3 Location settings are turned off. This status indicates that the user has not granted the application permission to access location.
NotInitialized 4 An operation to retrieve location has not yet been initialized. LocationStatus will have this value if the application has not yet called GetGeopositionAsync or registered an event handler for the PositionChanged event. LocationStatus may also have this value if your app doesn’t have permission to access location.
NotAvailable 5 Location services is not available on this version of Windows.
Method GetGeopositionAsync Starts an asynchronous operation to retrieve the current location of the device.
預設是 60 seconds timeout,除非設備有連接電源
如果請求時系統發現沒有任何 sensor 可以使用,則 7 seconds timeout
發生 timeout 時會觸發 StatusChanged event,並得到 NoData 狀態,而 PositionChanged event 不會被觸發。
  GetGeopositionAsync(TimeSpan,TimeSpan)

Starts an asynchronous operation to retrieve the current location of the device.
參數:

  • maximumAgeThe maximum acceptable age of cached location data. A TimeSpan is a time period expressed in 100-nanosecond units.
  • timeout:The timeout. A TimeSpan is a time period expressed in 100-nanosecond units.
  GetGeopositionHistoryAsync(DateTime)

Starts an asynchronous operation to retrieve the location history of the device.
參數:

  • startTime:Represents the beginning of the time span for which positions are to be returned.
  GetGeopositionHistoryAsync(DateTime,TimeSpan)

Starts an asynchronous operation to retrieve the location history of the device.
參數:

  • startTime:Represents the beginning of the time span for which positions are to be returned.
  • duration:Represents the length of time after startTime for which positions are to be returned.
  RequestAccessAsync Requests permission to access location data.
這是 Win10 才支援的 API,目的在操作用戶的座標資訊前先請求權限與宣告,通常會搭配系統的 URI 讓用戶選擇自動前往設定開始座標功能。例如:「ms-settings://privacy/location」。
Property DesiredAccuracy

Read/write. The accuracy level at which the Geolocator provides location updates.

  • High:only if your application requires the most accurate data available.
  • Default:optimize for power.

需注意有些設備不支援 High 的設定值,它會出現系統不支援的通知。

DesiredAccuracyInMeters 或 DesiredAccuracy 屬性被設定時App 將可以使用到誤差值 500 公尺內的座標資訊(default)。Default:500 公尺,High:10 公尺

當 App 同時指定了 DesiredAccuracy 與 DesiredAccuracyInMeters, 的話,系統會依照後面設定的為主

  DesiredAccuracyInMeters Read/write. Gets or sets the desired accuracy in meters for data returned from the location service.
提供更多 granularity (粒度)和 更高的位置精準度。
大部分程式只會用到 DesiredAccuracy 屬性即夠用。
  LocationStatus Read-only. The status that indicates the ability of the Geolocator to provide location updates.
  MovementThreshold Read/write. Gets and sets the distance of movement, in meters, relative to the coordinate from the last PositionChanged event, that is required for the Geolocator to raise a PositionChanged event.
  ReportInterval Read/write. The requested minimum time interval between location updates, in milliseconds.
If your application requires updates infrequently, set this value so that location services can conserve power by calculating location only when needed.

[注意]

  • 在 Win10 要使用定位服務前先藉由 RequestAccessAsync 方法在 foreground UI  thread 彈出詢問視窗。
  • 使用 emulator 要記得更動模擬器中的座標來觸發 PositionChanged event。

 

使用方法如下:

private Geolocator Locator;

 
private async void Button_Click(object sender, RoutedEventArgs e)
{
    // for win10
    // var accessStatus = await Geolocator.RequestAccessAsync();
    if (Locator == null)
    {
        Locator = new Geolocator();
        // 設定座標準確度等級,愈高愈耗電
        Locator.DesiredAccuracy = PositionAccuracy.Default;
        // 設定座標移動多少距離後要觸發 PositionChanged 事件,單位 公尺
        Locator.MovementThreshold = 100;
        // 設定準確度的誤差值,單位 公尺
        Locator.DesiredAccuracyInMeters = 500;
        Locator.PositionChanged += locator_PositionChanged;
        Locator.StatusChanged += locator_StatusChanged;
    }
    try
    {
        var currentLocation = await Locator.GetGeopositionAsync();
        if (currentLocation.CivicAddress != null)
        {
            txtAddress.Text = currentLocation.CivicAddress.ToString();
        }
        txtLat.Text = currentLocation.Coordinate.Latitude.ToString();
        txtLon.Text = currentLocation.Coordinate.Longitude.ToString();
    }
    catch (UnauthorizedAccessException ex)
    {
        // 代表沒有開放取得 GPS 座標的權限
    }
    catch (TimeoutException)
    {
        // 代表請求取得座標的時間超過了預設的 timeout 值
    }
    catch (TaskCanceledException) { }
}

 
void locator_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
{
    switch (args.Status)
    {
        case PositionStatus.Disabled:
            break;
        case PositionStatus.Initializing:
            break;
        case PositionStatus.NoData:
            // 通知用戶取不到 GPS 座標資訊
            break;
        case PositionStatus.NotAvailable:
            // 通知用戶該設備不支援該功能
            break;
        case PositionStatus.NotInitialized:
            break;
        case PositionStatus.Ready:
            break;
        default:
            break;
    }
}

 
void locator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
    // 取得最新的座標
    var newPosition = args.Position;
}

之前也有寫過相似的文章<Windows Phone 8 - Runtime Location API - 1>與<Windows Phone 8 - Runtime Location API - 2>可以參考看看,例如:

在座標更新時如何請求 cloud service 回應新的附件資料或是更新畫面地圖的位置。

 

 

 

大致瞭解 Geofence 的類別與相關監督的處理元件,以及如何取得設備坐標之後,往下説明如何注冊 Geofence 要處理的狀態與停留時間,

以及 App 如何處理 foreground/background 時對於 GeoMonitorManager 的操作。

 

1. 準備要建立的 Geofence 資料,以建立 Geocircle爲例;

   1: private void CreateGeofence()
   2: {
   3:     GeofenceMonitor.Current.Geofences.Clear();
   4:  
   5:     // 設定要監控的狀態
   6:     MonitoredGeofenceStates state = MonitoredGeofenceStates.Entered |
   7:                                     MonitoredGeofenceStates.Exited |
   8:                                     MonitoredGeofenceStates.Removed;
   9:     // 設定是否爲 SingleUse
  10:     Boolean isSingleUse = false;
  11:  
  12:     // 設定 DwellTime, StartTime 與 Duration
  13:     TimeSpan dwellTime = TimeSpan.FromSeconds(5);
  14:     DateTimeOffset startTime = DateTime.Now;
  15:     TimeSpan duration = TimeSpan.FromDays(1);
  16:  
  17:     BasicGeoposition pointPosition = new BasicGeoposition
  18:     {
  19:         Longitude = 121.5124,
  20:         Latitude = 25.0457,
  21:         Altitude = 13
  22:     };
  23:     // 建立 Geocircle, 并且 500 m 的範圍
  24:     Geocircle circle = new Geocircle(pointPosition, 500);
  25:     Geofence fence2 = new Geofence("fenceId2", circle, state, isSingleUse, dwellTime, startTime, duration);
  26:     GeofenceMonitor.Current.Geofences.Add(fence2);
  27: }

Geofence 提供 4 種類型可以使用,在建立時可以按照需要使用不同的 constructure,并且要加上特定屬性:

  •  MonitoredStates:那些是你要監控的,加入后會受到指定狀態的通知。
  • SingleUse flag:當 Geofence 指定要監控的 MonitoredStates 都被滿足后,自動刪除該 Geofence。
  • DwellTime:指定用戶需要在設定的區域内從進入到離開所待住的時間要多久
  • StartTime:何時啓動該 Geofence 的監控。
  • Duration:Geofence 持續監控的時間。

 

 

2. 請求用戶授權 App 取得定位權限,并于取得坐標后利用 GeofenceMonitor 注冊 Geofence;

   1: private async void OpenGeolocator(object sender, RoutedEventArgs e)
   2: {
   3:     // for win10
   4:     // var accessStatus = await Geolocator.RequestAccessAsync();
   5:     if (Locator == null)
   6:     {
   7:         Locator = new Geolocator();
   8:         // 設定座標準確度等級,愈高愈耗電
   9:         Locator.DesiredAccuracy = PositionAccuracy.Default;
  10:         // 設定座標移動多少距離後要觸發 PositionChanged 事件,單位 公尺
  11:         Locator.MovementThreshold = 100;
  12:         // 設定準確度的誤差值,單位 公尺
  13:         Locator.DesiredAccuracyInMeters = 500;
  14:         Locator.PositionChanged += locator_PositionChanged;
  15:         Locator.StatusChanged += locator_StatusChanged;
  16:     }
  17:     try
  18:     {
  19:         var currentLocation = await Locator.GetGeopositionAsync();
  20:         // 市政位址
  21:         if (currentLocation.CivicAddress != null)
  22:         {
  23:             txtAddress.Text = currentLocation.CivicAddress.ToString();
  24:         }
  25:         txtLat.Text = currentLocation.Coordinate.Latitude.ToString();
  26:         txtLon.Text = currentLocation.Coordinate.Longitude.ToString();
  27:     }
  28:     catch (UnauthorizedAccessException ex)
  29:     {
  30:         // 代表沒有開放取得 GPS 座標的權限
  31:     }
  32:     catch (TimeoutException)
  33:     {
  34:         // 代表請求取得座標的時間超過了預設的 timeout 值
  35:     }
  36:     catch (TaskCanceledException) { }
  37: }
  38:  

藉由指定  Geolocator 的坐標精準度與觸發坐標變化事件的距離,注冊 PositionChanged 與 StatusChanged 的事件。

 

 

3. 實做在 foreground 監控 GeofenceMointor 提供的事件,監控 Geofence 狀態的改變;

   1: void locator_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
   2: {
   3:     switch (args.Status)
   4:     {
   5:         case PositionStatus.Disabled:
   6:             // 用戶不允許存取座標資訊
   7:             break;
   8:         case PositionStatus.Initializing:
   9:             break;
  10:         case PositionStatus.NoData:
  11:             // 通知用戶取不到 GPS 座標資訊
  12:             break;
  13:         case PositionStatus.NotAvailable:
  14:             // 通知用戶該設備不支援該功能
  15:             break;
  16:         case PositionStatus.NotInitialized:
  17:             break;
  18:         case PositionStatus.Ready:
  19:             // foreground 注冊取得 GeofenceMonitor 的事件
  20:             var geofences = GeofenceMonitor.Current.Geofences;
  21:             RegistGenfenceMonitor();
  22:             break;
  23:         default:
  24:             break;
  25:     }
  26: }
  27:  
  28: // 開啓或關閉 GeofenceMonitor 的 GeofenceStateChanged / StatusChanged  事件
  29: private void RegistGenfenceMonitor(Boolean isRegist = true)
  30: {
  31:     if (isRegist)
  32:     {
  33:         GeofenceMonitor.Current.GeofenceStateChanged += Current_GeofenceStateChanged;
  34:         GeofenceMonitor.Current.StatusChanged += Current_StatusChanged;
  35:     }
  36:     else
  37:     {
  38:         GeofenceMonitor.Current.GeofenceStateChanged -= Current_GeofenceStateChanged;
  39:         GeofenceMonitor.Current.StatusChanged -= Current_StatusChanged;
  40:     }
  41: }
  42:  
  43: //
  44: void locator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
  45: {
  46:     // 隨著設備移動距離符合事件觸發的標準,取得最新的座標,
  47:     var newPosition = args.Position;
  48: }

監控在 Geolocator 狀態是 Ready 的時候開啓 GenfenceMonitor 事件的注冊,包括:GeofenceStateChanged 與 StatusChanged。

其中 GeofenceStateChanged  事件被觸發時,可以檢查是否符合我們預設的目標來提供對應的處理,例如:發出通知。

   1: /// <summary>
   2: /// 處理 GenfencMonitor 的狀態
   3: /// </summary>
   4: private void Current_StatusChanged(GeofenceMonitor sender, object args)
   5: {
   6:     switch (sender.Status)
   7:     {
   8:         case GeofenceMonitorStatus.Disabled:
   9:             break;
  10:         case GeofenceMonitorStatus.Initializing:
  11:             break;
  12:         case GeofenceMonitorStatus.NoData:
  13:             break;
  14:         case GeofenceMonitorStatus.NotAvailable:
  15:             break;
  16:         case GeofenceMonitorStatus.NotInitialized:
  17:             break;
  18:         case GeofenceMonitorStatus.Ready:
  19:             break;
  20:     }
  21: }
  22:  
  23: private void Current_GeofenceStateChanged(GeofenceMonitor sender, object args)
  24: {
  25:     var lastGeoposition = sender.LastKnownGeoposition;
  26:     var reports = sender.ReadReports();
  27:     foreach (var item in reports)
  28:     {
  29:         if (item.Geofence.Id == "fenceId2")
  30:         {
  31:             // 判斷是否爲完成設備進入指定區域并待上指定的時間
  32:             if (item.NewState == GeofenceState.Entered || item.NewState == GeofenceState.Exited)
  33:             {
  34:                 // send notification to ui thread
  35:                 NotificationManager.Notify("Geofence Meet", "Device exists in the area.");
  36:             }
  37:             else if (item.NewState == GeofenceState.Removed)
  38:             {
  39:                 // notify remove the Geofence
  40:                 NotificationManager.Notify("Geofence remove", "Geofence be removed.");
  41:             }
  42:             break;
  43:         }
  44:     }
  45: }

上述是在 foreground 處理 Geolocator 與 GeofenceMonitor 的任務,最重要就是注冊自己預期檢測的 Geofence 與 處理 GeofenceMonitor

的 GeofenceStatusChanged 事件,才能符合完成任務。更多的詳細内容可以參考<Handle geofence notifications in the foreground (XAML)>。

 

4. 實作 Background Task 搭配取得坐標資訊與搭配 GeofenceMonitor 觸發事件發出消息;

4-1. 建立一個 Background Task 專門處理 GeolocationMonitor 中已經保存的 Geofence 狀態的改變資訊。

   1:  
   2: public sealed class GeofenceBackgroundTask : IBackgroundTask
   3: {
   4:     CancellationTokenSource cts = null;
   5:  
   6:     public GeofenceBackgroundTask()
   7:     {
   8:     }
   9:  
  10:     async void IBackgroundTask.Run(IBackgroundTaskInstance taskInstance)
  11:     {
  12:         BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
  13:  
  14:         try
  15:         {
  16:             // Associate a cancellation handler with the background task.
  17:             taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
  18:  
  19:             // Get cancellation token
  20:             if (cts == null)
  21:             {
  22:                 cts = new CancellationTokenSource();
  23:             }
  24:             CancellationToken token = cts.Token;
  25:  
  26:             // Create geolocator object
  27:             Geolocator geolocator = new Geolocator();
  28:  
  29:             // Make the request for the current position
  30:             Geoposition pos = await geolocator.GetGeopositionAsync().AsTask(token);
  31:  
  32:             GetGeofenceStateChangedReports(pos);
  33:         }
  34:         catch (UnauthorizedAccessException)
  35:         {
  36:             WipeGeofenceDataFromData("Status",
  37:                 "Location Permissions disabled by user. " +
  38:                 "Enable access through the settings charm to enable the background task.");
  39:             WipeGeofenceDataFromData("GeofenceEvent", "");
  40:         }
  41:         finally
  42:         {
  43:             cts = null;
  44:  
  45:             deferral.Complete();
  46:         }
  47:     }
  48:  
  49:     private void WipeGeofenceDataFromData(String key, String value)
  50:     {
  51:         var settings = ApplicationData.Current.LocalSettings;
  52:         settings.Values[key] = value;
  53:     }
  54:  
  55:     private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
  56:     {
  57:         if (cts != null)
  58:         {
  59:             cts.Cancel();
  60:             cts = null;
  61:         }
  62:     }
  63:  
  64:     private void GetGeofenceStateChangedReports(Geoposition pos)
  65:     {
  66:         GeofenceMonitor monitor = GeofenceMonitor.Current;
  67:         Geoposition posLastKnown = monitor.LastKnownGeoposition;
  68:         bool eventOfInterest = true;
  69:  
  70:         if (true == eventOfInterest)
  71:         {
  72:             if (true == eventOfInterest)
  73:             {
  74:                 string geofenceItemEvent = null;
  75:  
  76:                 int numEventsOfInterest = 0;
  77:  
  78:                 // Retrieve a vector of state change reports
  79:                 var reports = GeofenceMonitor.Current.ReadReports();
  80:  
  81:                 foreach (GeofenceStateChangeReport report in reports)
  82:                 {
  83:                     GeofenceState state = report.NewState;
  84:  
  85:                     geofenceItemEvent = report.Geofence.Id + " ";
  86:  
  87:                     if (state == GeofenceState.Removed)
  88:                     {
  89:                         GeofenceRemovalReason reason = report.RemovalReason;
  90:  
  91:                         if (reason == GeofenceRemovalReason.Expired)
  92:                         {
  93:                             geofenceItemEvent += " (Removed/Expired)";
  94:                         }
  95:                         else if (reason == GeofenceRemovalReason.Used)
  96:                         {
  97:                             geofenceItemEvent += " (Removed/Used)";
  98:                         }
  99:                     }
 100:                     else if (state == GeofenceState.Entered)
 101:                     {
 102:                         geofenceItemEvent += " (Entered)";
 103:                     }
 104:                     else if (state == GeofenceState.Exited)
 105:                     {
 106:                         geofenceItemEvent += " (Exited)";
 107:                     }                        
 108:  
 109:                     ++numEventsOfInterest;
 110:                 }
 111:  
 112:                 if (true == eventOfInterest && 0 != numEventsOfInterest)
 113:                 {
 114:                     // NOTE: Other notification mechanisms can be used here, such as Badge and/or Tile updates.
 115:                     DoToast(numEventsOfInterest, geofenceItemEvent);
 116:                 }
 117:             }
 118:         }
 119:     }
 120:  
 121:     /// <summary>
 122:     /// Helper method to pop a toast
 123:     /// </summary>
 124:     private void DoToast(int numEventsOfInterest, string eventName)
 125:     {
 126:         // pop a toast for each geofence event
 127:         ToastNotifier ToastNotifier = ToastNotificationManager.CreateToastNotifier();
 128:  
 129:         // Create a two line toast and add audio reminder
 130:  
 131:         // Here the xml that will be passed to the 
 132:         // ToastNotification for the toast is retrieved
 133:         Windows.Data.Xml.Dom.XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
 134:  
 135:         // Set both lines of text
 136:         Windows.Data.Xml.Dom.XmlNodeList toastNodeList = toastXml.GetElementsByTagName("text");
 137:         toastNodeList.Item(0).AppendChild(toastXml.CreateTextNode("Geolocation Sample"));
 138:  
 139:         if (1 == numEventsOfInterest)
 140:         {
 141:             toastNodeList.Item(1).AppendChild(toastXml.CreateTextNode(eventName));
 142:         }
 143:         else
 144:         {
 145:             string secondLine = "There are " + numEventsOfInterest + " new geofence events";
 146:             toastNodeList.Item(1).AppendChild(toastXml.CreateTextNode(secondLine));
 147:         }
 148:  
 149:         // now create a xml node for the audio source
 150:         Windows.Data.Xml.Dom.IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
 151:         Windows.Data.Xml.Dom.XmlElement audio = toastXml.CreateElement("audio");
 152:         audio.SetAttribute("src", "ms-winsoundevent:Notification.SMS");
 153:  
 154:         ToastNotification toast = new ToastNotification(toastXml);
 155:         ToastNotifier.Show(toast);
 156:     }
 157: }

這些資訊可以協助處理判斷有哪些 Geofence 已經完成了指定監控的 state。

 

4-2. 將建立好的 Background Task 在 App 裏面加以注冊。

   1: <Extensions>
   2:     <Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTaskLocation.GeofenceBackgroundTask">
   3:       <BackgroundTasks>
   4:         <m2:Task Type="location" />
   5:       </BackgroundTasks>
   6:     </Extension>
   7:   </Extensions>

將建立好的 Background Task 加入 Package.appxmanifest 檔案。

   1: private const string SampleBackgroundTaskName = "GeofenceBackgroundTask";
   2: private const string SampleBackgroundTaskEntryPoint = "BackgroundTaskLocation.GeofenceBackgroundTask";
   3: private IBackgroundTaskRegistration geofenceTask = null;
   4:  
   5: private async void OnRegistBackgroundTaskLocation(object sender, RoutedEventArgs e)
   6: {
   7:     // 請求取得 Background Task 的權限。
   8:     // 如果用戶已經同意過依然無法取得權限的話,需要的 設定中的定位 權限檢查是否有手動關閉對於這個 App 的權限
   9:     BackgroundAccessStatus backgroundAccessStatus = await BackgroundExecutionManager.RequestAccessAsync();
  10:  
  11:     // 建立一個新的 background task
  12:     BackgroundTaskBuilder geofenceTaskBuilder = new BackgroundTaskBuilder();
  13:     geofenceTaskBuilder.Name = SampleBackgroundTaskName;
  14:     geofenceTaskBuilder.TaskEntryPoint = SampleBackgroundTaskEntryPoint;
  15:  
  16:     // 建立一個新的 location trigger
  17:     var trigger = new LocationTrigger(LocationTriggerType.Geofence);
  18:  
  19:     // 將 location trigger 與 background task 建立起關聯
  20:     geofenceTaskBuilder.SetTrigger(trigger);
  21:  
  22:     // 如果 background task 在 oncompleted 時需要 user presence 與 internet connection, 則需要加入以下宣告
  23:     // SystemCondition condition = new SystemCondition(SystemConditionType.UserPresent | SystemConditionType.InternetAvailable);
  24:     // geofenceTaskBuilder.AddCondition(condition);
  25:  
  26:     // 注冊 background task
  27:     geofenceTask = geofenceTaskBuilder.Register();
  28:  
  29:     // 建立 new background task 的 completed event handler
  30:     geofenceTask.Completed += new BackgroundTaskCompletedEventHandler(OnCompleted);
  31:  
  32:     switch (backgroundAccessStatus)
  33:     {
  34:         case BackgroundAccessStatus.Unspecified:
  35:         case BackgroundAccessStatus.Denied:
  36:             NotificationManager.Notify("Not able to run in background.", "");
  37:             break;
  38:  
  39:         default:
  40:             // Ensure we have presented the location consent prompt (by asynchronously getting the current
  41:             // position). This must be done here because the background task cannot display UI.
  42:             OnGetGeopositionAsync(sender, e);
  43:             break;
  44:     }
  45: }
  46:  
  47: async private void OnCompleted(IBackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs e)
  48: {
  49:     if (sender != null)
  50:     {
  51:         // Update the UI with progress reported by the background task
  52:         await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  53:         {
  54:             try
  55:             {
  56:                 // If the background task threw an exception, display the exception in
  57:                 // the error text box.
  58:                 e.CheckResult();
  59:  
  60:                 // Update the UI with the completion status of the background task
  61:                 // The Run method of the background task sets the LocalSettings. 
  62:                 var settings = ApplicationData.Current.LocalSettings;
  63:             }
  64:             catch (Exception)
  65:             {
  66:                 // The background task had an error
  67:             }
  68:         });
  69:     }
  70: }

以上加入注冊 Background Task 到背景原件裏面,藉由 BackgroundExecutionManager.RequestAccessAsync(); 取得用戶的權限,

 

 

相關測試的方式可以參考<Test and debug your geofencing apps>。

 

[範例程式]

======

以上分享如何注冊 Geofence 與 GeofenceMonitor 搭配 Geolocator 來識別設備是否符合監控的 geofence。

感覺這個很多的題目可以實現,希望簡單的介紹對大家有所幫助,謝謝。

 

References:

Geofencing, start to finish (XAML)

Detect the user's location & geolocation sample

Respond to location updates

Guidelines for geofencing / Guidelines for using sensitive devices / Set up a geofence

Handle geofence notifications in the foreground

Listen for geofence events in the background

Handle geofence notifications from a background task

Test and debug your geofencing apps

Creating a Location-Aware App with Geofencing

Guidelines for location-aware apps / Display your location using Bing Maps

Windows 10 Insider Preview geolocation sample / Windows 8.1 geolocation sample

Building Cross-Platform iBeacon Apps for iOS, Android and Windows with C# and Xamarin

Windows 8.1 Preview: Geo-Fencing TriggersSupporting your app with background tasks (XAML)

Geolocation sample & Windows-universal-samples / Samples / Geolocation /  (UWP)

Listen for geofence events in the background

Respond to location updates (XAML)

Handle geofence notifications from a background task (XAML)

GeofenceMonitor.StatusChanged

 

Technorati Tags: ,,