Windows 7傳感器 & 定位平台可以使你的應用程序更加適應當前的環境, 並且能夠改變它們的外觀,體驗或者行為。這有一些例子:
• 陽光明媚的時候,你在戶外使用一個移動PC(比如,筆記本電腦或者 平板電腦),那麼應用程序可能會增加亮度,對比度來增加屏幕的易讀性。
• 應用程序可能會提供一些特定的信息,比如附近的餐館。
• 應用程序可能會像游戲控制器一樣使用3D加速計和按鈕。
• 應用程序也可能會使用人體傳感器來改變Messenger的登錄狀態。
圖例 1
可調MSDN閱讀器,使用了環境光線感應器來調整對比度,尺寸 和顏色飽和度
傳感器&定位平台,對所有的解決方案都有很多優點:
• 硬件-獨立:不再需要為一個獨立的提供商的API進行學習和投資 ;所有的傳感器類型控制起來都非常相似。
• 隱私:Microsoft意識 到,傳感器和本地數據都是私人的,個人的驗證信息,所有的傳感器在默認情況 下都是關閉的。你可以通過控制面板隨時打開/關閉傳感器。應用程序將會提示你 一個安全的允許的UI用戶界面,來打開指定的傳感器。
• 應用程序共享:多個應用程序可以同時使用同一個傳感器的數據。
• 定位簡單化:定位API能夠使你不需要關心獲取信息的特殊結構, 就能得到所需要的位置。比如。GPS,發射塔或者WIFI熱點。定位API將自動選擇 可用的最正確的傳感器數據。另外,你也不需要去實現類似於NMEA的GPS協議了。
目的
在本次動手實驗中,你將了解到如何在你的應用程序中,整 合Windows 7的傳感器支持,包括:
• 檢測已連接的傳感器
• 檢測傳感器狀態
• 如果所連接的傳感器沒有開啟, 那麼向用戶提出一個許可
• 處理傳感器的數據事件
系統需求
若完成此實驗,你必須需要以下組件:
• Microsoft Visual Studio 2008 SP1
• Windows 7 Windows 7 SDK
• 安裝 了驅動的Windows 7兼容環境光線傳感器硬件,或者虛擬環境光線傳感器
練習: 根據環境光線的強烈程度調整字體
在本次練 習中,你需要修改一個Win32 MFC應用程序,以便於可以根據環境光線感應器所感 應到得不同的光線強度級別,來調整相應的字體。基本上大部分的應用程序用戶 界面都已經實現了;你只需要在缺少的部分與Windows 7 的傳感器API進行交互。 那麼最終結果類似於下面的圖片:
開始本次練習之前,請在Visual Studio中打開AmbientLightAware解決方案( 在HOL的根目錄中)。
這是一個簡單而標准的MFC應用程序。花一兩分鐘去 浏覽一下包含示例的應用程序的C++文件(.cpp/.h)。為了方便你,請確認任務 列表工具窗口是可見的(在View菜單中,選擇任務列表),並且在工具窗口復選 框選擇注釋–你將看到這個練習中的每一個任務中的TODO事項。運行這個應 用程序,使你對它有所熟悉。
注意
以簡短和簡單為目的,示例應用程序沒有展示最好的MFC UI用戶界面,也 沒有為面向對象開發和COM開發展現出最好的設計標准。
任 務 1 —添加環境光線感知
在這個任務中,你需要添加一些支持的類 ,並且修改你的應用程序,使其能夠根據一個或多個環境光線傳感器感應到的平 均光線明視度,來調整字體。
1.右鍵點擊AmbientLightAware項目,然後 點擊屬性
a.轉向Linker --> Input --> Additional dependencies.
b.向列表中添加 sensorsapi.lib 。
c.點擊OK關閉 對話框。
2.找到StdAfx.h文件,然後將下面的代碼取消注釋:
C++
#include <Sensors.h>
#include <SensorAPI.h>
3.右鍵點擊AmbientLightAware項目,然後 點擊Add --> Existing Item...
a.如果你不在項目目錄中,那麼就導航到項目目錄中。
b.選擇下面的 文件(按住Ctrl選擇):
• AmbientLightAwareSensorManagerEvents.cpp
• AmbientLightAwareSensorManagerEvents.h
• AmbientLightAwareSensorEvents.cpp
• AmbientLightAwareSensorEvents.h
c.點擊Add 按鈕。
傳感器API 是由IsensorManager對象構成,IsensorManager對象可以用來對現有的傳感器根 據它們的類型,類別或者ID進行查詢。你也可以向其他的用戶發送請求,來要求 使用它們的傳感器。除此之外,你還可以當新的傳感器變為可用時,通知自己。
CambientLightAwareSensorManagerEvents類提供了兩個功能:
• 它包含了與傳感器管理器間的交互代碼
• It also implements the OnSensorEnter (new sensor becomes available) event它還實 現了OnSensorEnter(當新的傳感器變為可用)事件。
從傳感器管理器中 ,你可以獲取到Isensor對象,它用來表示獨立的傳感器。你可以查詢一個傳感器 的狀態,並且獲取或者設置其他的屬性,比如制造廠商,型號,精度和更新周期 。你也可以請求一個包含傳感器所感應到的結果度量的數據報表。你同樣可以注 冊一些事件(當傳感器變為不可用,狀態更改,有新的數據報表,一般事件)。
CambientLightAwareSensorEvents類提供了兩個功能:
• 它 包含了與環境光線傳感器進行交互的代碼
• 它同樣實現了上述的傳感器事件
4.導航到 AmbientLightAwareSensorManagerEvents.cpp文件的頂部。
5.檢查構造函 數,AddRef, Release 和 QueryInterface方法。它們僅僅是標准的COM樣本代碼 。
6.將Initialize()方法取消注釋。
a.這段代碼創建了一個傳感 器管理器對象(等效於CoCreateInstance)。
b.SetEventSink的調用,是 為我們訂閱OnSensorEnter事件(實現這個事件的類)。
c.通過調用 GetSensorsByType,我們可以枚舉出所有類型為SENSOR_TYPE_AMBIENT_LIGHT的傳 感器。每一個傳感器都擁有三個GUID:類別,類型和ID。類別用來表示這個傳感 器是用來衡量什麼的(例如,環境),類型是用來表示傳感器是如何進行衡量的 (比如,溫度,濕度)。 ID則是傳感器的唯一標識。ID可能是永久的(如果在傳 感器上的序列號是“燒錄”上的),或者是非永久的。
d.SENSOR_TYPE_AMBIENT_LIGHT定義了一個GUID地圖。在sensors.h中,有 很多預先定義好的傳感器類型。你可以在SENSOR_TYPE_AMBIENT_LIGHT定義上右鍵 點擊,然後選擇Go To Definition去查看這些類型。
e.我們通過調用 ISensorManager::RequestPermissions()來請求使用環境光線傳感器,特別是父 HWND,傳感器集合,還有是否需要在用戶選擇該如何去做之前,阻止該方法。這 些操作會顯示下面的對話框:
如果用戶拒絕開啟一個傳感器,那麼後續的調用RequestPermissions的方法將 不會再次顯示這個UI用戶界面。
f.GetSensorsByType將返回一個 IsensorCollection對象,這個對象中的元素數量由GetCount()決定,並且可以使 用GetAt()來訪問。
g.每個傳感器,我們都需要調用AddSensor方法,這個 方法我們後面將會實現。
h.由於我們希望強制生成最新的數據報告,所以 我們需要調用傳感器事件類中的GetSensorsData()方法。否則,傳感器在亮度變 化不是很大的情況下,不會產生數據報告。
7.將AddSensor()方法取消注 釋。
a.Isensor中的SetEventSink方法,將為我們注冊 CambientLightAwareSensorEvents對象,這個對象是我們在這個指定的傳感器事 件(狀態變更,傳感器已分離/不可用,或者新的數據報告)的構造函數中創建的
b.傳感器對象添加到m_Sensors CatlMap 。
8.將兩個 RemoveSensor()方法取消注釋。
a.將包含Isensor*的方法,取消注釋。
當應用程序需要退訂指定傳感器時,需要調用這個方法。當應用程序關閉 的時候,將會調用這個方法。
b.將包含REFSENSOR_ID的方法取消注釋。
當傳感器被強制移除時(比如,當硬件斷開連接時),傳感器事件類 (CAmbientLightAwareSensorEvents)將調用這個方法。這個方法能得到一個 GUID而不是一個ISensor*,因為當傳感器被移除後,將不能繼續訪問。
9. 將UnInitiliaze()方法取消注釋。這個方法將取消事先已經訂閱到傳感器的所有 處理的事件。它同樣也會取消訂閱傳感器管理器事件。這個方法將會在關閉的時 候被調用。
10.將OnSensorEnter()方法取消注釋。
a.這個方法將實現 IsensorManagerEvents。它將在新傳感器變為可用時被調用(比如,物理連接)
b.當Initialize()管理一些已經在應用程序啟動時就可用的傳感器時,這 個方法將會管理傳感器,使其稍後才變為可用。
c.這個方法還會請求傳感 器獲取其狀態,以便於判斷當前它是否已經可用,並且請求一個數據報告。
11.導航到 AmbientLightAwareSensorEvents.cpp 文件的頂部。
12.檢查構造函數,AddRef, Release 和 QueryInterface方法。他們僅僅 是標准COM樣本代碼。
13.檢查m_mapLux。它是一個(浮點型SENSOR_ID) 的CatlMap。這個地圖將跟蹤每一個傳感器的最新的環境光線值的記錄(Lux單元 )。這其中的每個傳感器的每個值,都是獨立保存的,所以他們才能在後期進行 平均計算。
14.將OnLeave()方法取消注釋。這個方法是實現 IsensorEvents和當傳感器變為不可用時調用的(斷開連接)。
a.這個方 法將調用我們之前已經實現了的方法 CAmbientLightAwareSensorManagerEvents::RemoveSensor()。
b.這個方 法將刪除被移除的傳感器的最新存儲的環境光線值,那麼這些被刪除的值在後面 進行平均計算時,就不會被計算在內。UpdateLux()方法是用來反映計算出的光線 亮度平均值和更新UI用戶界面的。
15.將OnStateChanged()方法取消注釋 。這個方法實現了IsensorEvents,並且當一個傳感器的狀態發生變化時,將被調 用。當傳感器已經准備就緒,那麼會提供一個數據報告,並且UI用戶界面也會被 更新。
a.即使傳感器是連接著的,它也可能不能使用(SENSOR_STATE_READY)。這個 狀態將告訴你為什麼不能使用的原因。
b.另外一個重要的狀態是 SENSOR_STATE_ACCESS_DENIED,它表示能夠使用當前傳感器的權限,還沒有在控 制面板中進行設置。當你設置了權限後,這兒狀態將更改。你也可以調用 ISensorManager::RequestPermissions來顯示一個權限對話框。
c.你可以 調用ISensor::GetState()方法,來查詢所有的狀態。
16.將 OnDataUpdated()方法取消注釋。這個方法將實現IsensorEvents,並且當傳感器 有新的可用的數據報告時將被調用。一些傳感器將定期進行更新,其他的當數據 的值發生了很大的變化才會進行更新。在一些傳感器中,你可以通過設置 SetProperties()中的 SENSOR_PROPERTY_CHANGE_SENSITIVITY屬性,來控制傳感 器的敏感度。
17.將OnEvent()取消注釋。這個方法實現了IsensorEvents ,並且當其它事件,包括OnLeave(), OnStateChanged() 和 OnData()激發時會被 調用。
a.你可以通過事件的GUID對事件進行區分。
b.這個事件存 在的意義,就在於它能允許傳感器自定義用戶事件(例如,GPS傳感器可能需要有 衛星發現/丟失事件)。在這裡,我們沒有自定義事件,所以這個方法總是會返回 S_OK。
18.將UpdateLux()方法取消注釋。這個方法將重復訪問m_mapLux 地圖和計算平均值。它會調用CambientLightAwareDlg類中的一個同名方法。
19.將UpdateData(ISensor* pSensor)方法取消注釋。注意這裡還有一個 同樣名稱,只是重載參數不同的方法。
a.無論何時我們需要手動請求一個新的數據報告時,這個方法都會貫穿整個代 碼。
b.這個方法調用ISensor::GetData()生成一個IdataReport對象。這 個對象將被當做第二個參數,在UpdateData調用時重載。它還將保存代碼的副本 。
20.將GetSensorData(ISensor *pSensor, ISensorDataReport *pDataReport)方法取消注釋。這個方法將實現IsensorEvents,並且在當傳感器 有新的可用的數據時被調用。
a.傳感器的數據報告由一個或多個數據(區 域)構成。它們可以是由多個類型的PROPVARIANT來支持。
b.你可以右鍵 點擊SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX,然後選擇轉到定義,去查看預定的屬 性。這些屬性在sensors.h文件,按照傳感器類別組合在一起。緊鄰每一個屬性的 注釋,是其指定的數據類型。
c.你可以通過調用GetSensorValues()方法 ,來決定在數據報告中需要顯示那些數據,這個方法將返回一個鍵值的集合。
d.如果傳感器支持調用ISensor::SupportsDataField()來給出數據,那麼 你也可以來決定其優先級。
e.使用PropVariantInit 和 PropVariantClear 方法來初始化成PROPVARIANT。
f.這個方法將更新 m_mapLux集合中的傳感器的亮度值的集合,然後調用UpdateLux()重新計算,並且 更新UI用戶界面。
21.導航到AmbientLightAwareDlg.h文件的頂部。將 CleanUp() 和 UpdateLux()方法的聲明取消注釋。
22.導航到文件的底 部。將m_pSensorManagerEvents 和 m_lfLogFont的類成員取消注釋。
23.導航到 AmbientLightAwareDlg.cpp文件的頂部。將下面的 #include 語句 取消注釋:
C++
#include "AmbientLightAwareSensorEvents.h"
#include "AmbientLightAwareSensorManagerEvents.h"
24.導 航到 AmbientLightAwareDlg 構造函數。將初始化的m_pSensorManagerEvents 和 m_lfLogFont取消注釋。
25.取消 CleanUp()方法的注釋。這個方法將在關 閉的時候被調用。
26.將InitAmbientLightAware()方法取消注釋。這個方 法將在啟動時被調用,來初始化相關的傳感器。
27.導航到 OnInitDialog(),將調用InitAmbientLightAware()的方法取消注釋。
28. 將 UpdateLux()方法取消注釋。這個方法決定了在當前的亮度下所顯示的字體。 它將更新UI用戶界面的成員(傳感器的平均亮度)和UI用戶界面。一個適當的應 用程序將可以實現低通過濾器來避開可能會引起用戶煩躁的頻繁的字體更新。
29.導航到 OnPaint(),將負責改變字體的IDC_STATIC_SAMPLE靜態控制代 碼取消注釋。
30.導航到 AmbientLightAware.cpp文件的頂部。將下面的 #include 代碼取消注釋:
(C++)
#include "AmbientLightAwareSensorEvents.h"
#include "AmbientLightAwareSensorManagerEvents.h"
31.導航到 InitInstance()。將調用 CoInitializeEx(), CoUninitialize() 和 dlg.CleanUp()方法的代碼取消注釋。
32.編譯並運行應用程序。改變亮度 值,然後查看文本的變大和縮小。
摘要
在這個實驗中國,你已經體會到了使用Windows 7 傳感器 API,向一個現有的應用程序中整合傳感器的支持。
積累一個成熟的支持 Windows 7的應用程序,可能不僅僅需要本次試驗中所做的工作,但是,現在你已 經可以有充足的准備去自己解決了。