1、說明
標准的Sideshow設備連接PC後,由PC上Sideshow管理中心負責向Sideshow設備添加Gadget應用,該 Gadget實例被Sideshow設備緩存到內存中。當Sideshow設備未連接PC,掉電重啟後,所有原先添加的 Gadget信息將全部丟失。
本功能的目的就是,Sideshow設備在上述情況下重啟,可以自動恢復原先的Gadget應用。
2、Sideshow功能模塊關系圖
SideShow設備啟動後,主界面優先啟動,顯示一些本地Gadget和系統基本信息(日期時間等)。於此 同時Message組件也開始工作,全權接管和PC Sideshow相關的通信功能,新的Gadget也就是通過該通道添 加到Sideshow設備中來,新的畫面、信息、狀態也源源不斷由PC向SideShow設備而來,此時SideShow設備 也會根據Gadget的操作情況,主動向PC Sideshow管理中心回傳gadget的狀態信息(包括是否獲得焦點、 按鍵信息、菜單選擇信息等等)。
當Gadget相關信息由PC過來時,會先存儲到Cache中去,並不直接顯示相關畫面,UI引擎根據需要,會 隨時從Cache中獲取相關界面元素。
3、Gadget本地存儲實現
3.1、實現思路
雖然所有的UI信息都緩存到Cache中,但是由於顯示Gadget是一個動態過程,是顯示引擎根據需要,動 態從Cache中構建Gadget。而該需要就是Message組件通知並促成的。如果僅僅把Cache的內存持久化,而 不啟動消息循環引擎,Gadget是無法加載並應用的。
一切的根源來至於Message組件中的各種不同的消息,所以最直接的思路就是截獲並持久化相關消息, 並在設備啟動時(要先判斷是否聯機,如果已聯機,則不執行任何代碼),讀取並投送相關消息,模擬 Sideshow設備聯機時的相關信息交互,從而實現了Gadget非聯機狀態情況下的動態加載,由於是通過模擬 消息方式實現了Gadget的加載,所以此時Gadget的功能和聯機時無異(注意:由於沒有聯機,所以Gadget 顯示的信息和內容是非即時的)。
3.2、具體實現
3.2.1 消息結構簡介
PC和Sideshow設備相關的消息共74種,消息結構分兩部分,一是消息頭(包含消息指令),二是 Payload(負荷數據),我們要緩存的數據也主要是這兩部分。
詳情可參見《SideShow通信協議》相關文檔。
3.2.2 需要持久化的消息指令
序號 名稱 值 說明 1 AddApp 0x010D 添加gadget 2 DeleteApp 0x010E 刪除指定gadget 3 DeleteAllApps 0x010F 刪除全部gadget 4 AddContentItem 0x0114 添加指定gadget內容項 5 DeleteContentItem 0x0115 刪除指定gadget內容項 6 DeleteAllContentItems 0x0116 刪除指定gadget所有內容項注:考慮到性能,第5項和第6項消息沒有做相關的處理。
3.2.3 本地存儲格式
首先創建Gadget目錄("ROOT"CADGET_CACHE)
每一個Gadget應用對應一個由其GUID為命名的目錄,目錄中存放兩類信息,一是:index.App,該文件 存放了Gadget的名字和圖標相關信息;二是Gadget內容信息,命令格式 {內容ID}.Content。
3.2.4 Gadget持久化類代碼
public class GadgetCache
{
public static bool OnlineFlag = false;
public static bool RunFlag = false;
public static int CheckCachedDirectory();
public static string[] EnumApps();
public static string[] EnumContentItems(string AppId);
public static int LoadApp(string AppId, out byte[] bytData);
public static int SaveApp(string AppId, byte[] bytData);
public static int DeleteApp(string AppId);
public static int DeleteAllApps();
public static int LoadContentItem(string AppId, string ContentId, out byte[] bytData);
public static int SaveContentItem(string AppId, string ContentId, byte[] bytData);
public static int DeleteContentItem(string AppId, string ContentId);
public static int DeleteAllContentItems(string AppId);
}
詳細實現代碼 略
代碼所在的文件:PcMessageHandler.cs
3.2.5 Gadget持久化執行代碼
case PacketType.AddApp:
{
ClientInfo info = new ClientInfo();
Guid appId = command.ReadGuid();
Guid endptId = command.ReadGuid();
info.Name = command.ReadString();
info.Policy = (CachePolicy) command.ReadInt();
info.OnLineOnly = command.ReadUint() != 0;
info.Icon48Data = command.ReadByteArray();
info.Icon32Data = command.ReadByteArray();
info.Icon16Data = command.ReadByteArray();
shell.ConnectClient(appId, endptId, info);
if (!GadgetCache.RunFlag) GadgetCache.SaveApp(appId.ToString(), msg.Payload);
break;
}
case PacketType.AddContentItem:
{
Guid appId = command.ReadGuid();
Guid endptId = command.ReadGuid();
uint contentId = command.ReadUint();
byte[] itemData = command.ReadByteArray();
cacheMgr.AddItem(appId, endptId, contentId, itemData);
if (!GadgetCache.RunFlag) GadgetCache.SaveContentItem (appId.ToString(), contentId.ToString(), msg.Payload);
break;
}
case PacketType.DeleteContentItem:
case PacketType.DeleteAllContentItems:
(其它代碼 略)
代碼所在的文件:PcMessageHandler.cs 中的ProcessMessage函數
3.2.6 Gadget本地數據讀取及恢復
1、啟動
new Thread(new ThreadStart(Load_Gadget)).Start();
代碼所在的文件:PcMessageHandler.cs 中的Initialize函數
2、數據讀取及恢復
void Load_Gadget()
{
Thread.Sleep(3000); //wait 3s
try
{
if (!GadgetCache.OnlineFlag)
{
Globals.YFShowInfo("Load ...", "Load_Gadget", Globals.YFInfoType.Level_8);
GadgetCache.RunFlag = true;
string[] AppIds = GadgetCache.EnumApps();
byte[] bytData = null;
foreach (string AppId in AppIds)
{
if (GadgetCache.OnlineFlag)
{
GadgetCache.RunFlag = false;
return;
}
GadgetCache.LoadApp(AppId, out bytData);
ProcessMessage(GadgetCache.ToAuxMessage(269, bytData));
string[] ContentIds = GadgetCache.EnumContentItems(AppId);
foreach (string ContentId in ContentIds)
{
if (GadgetCache.OnlineFlag)
{
GadgetCache.RunFlag = false;
return;
}
GadgetCache.LoadContentItem(AppId, ContentId, out bytData);
ProcessMessage(GadgetCache.ToAuxMessage(276, bytData));
}
}
GadgetCache.RunFlag = false;
Globals.YFShowInfo("End", "Load_Gadget", Globals.YFInfoType.Level_8);
}
}
catch
{
GadgetCache.RunFlag = false;
Globals.YFShowInfo("Load ...Error", "Load_Gadget", Globals.YFInfoType.Level_8);
}
}
代碼所在的文件:PcMessageHandler.cs 中的PcMessageHandler類
4、其它說明
Sideshow SDK自帶的Sideshow模擬器竟然不支持文件系統,所以為了便於調試(在硬件平台上測試是 很費時間的),我又擴展了我的MF模擬器,讓它也可以支持Sideshow了,不過唯一不爽的是,添加該功能 後,以後所有的MF應用程序必須要求VS2008具有管理員權限。
好處是我的MF模擬器可以模擬Sideshow設備連線和掉線,這一點官方的Sideshow模擬器是不支持的, 有了這個功能,我才方便測試我的Gadget本地恢復。
這是非聯機模式,注意Sideshow界面的右上角沒有系統聯機的狀態信息,此外,仔細看,一些非本地 Gadget也出現在界面中的了,這時它們的操作和聯機一樣。
這是聯機模式,此時出現的Gadget是Sideshow控制面板選中的Gadget。注意界面的右上角出現了聯機 狀態。
說明:Sideshow連接中的COM僅僅是一個通道借用,目的是采用串口的操作函數進行數據讀寫,其實物 理串口並沒有數據。標准Sideshow模擬器連接的是COM33,我的模擬器和實際設備一致,選用的是COM3。