天天看點

Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版

一連寫了四篇筆記,介紹了Live SDK,也實際玩過取得使用者Live ID身分、連上SkyDrive等把戲:

  • Live SDK筆記1-簡介
  • Live SDK筆記2-註冊App與基本術語
  • Live SDK筆記3-使用Live ID登入ASP.NET網站
  • Live SDK筆記4-SkyDrive Explorer, ASP.NET版

埋了這些哽,其實背後我最想做的,是從WP7程式連上SkyDrive!

手機基於儲存空間的限制,以及跨裝置/平台共享的需求,非常需要結合網路儲存空間做為後盾,而WP7配上微軟體系的SkyDrive,感覺上是很棒的組合。

Live SDK有提供WP7專用的元件庫,下載安裝Live SDK後,可在WP7專案參照中找到Microsoft.Live及Microsoft.Live.Controls,學習中心有篇WP7整合Live SDK教學,是很好的入門。

Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版

Live SDK提供SignInButton控制項,表面上是顆登入登出鈕,背地則幫我們處理掉在App內嵌瀏覽器載入Live帳號登入網頁以及同意網頁等繁瑣細節,是整合Live SDK最簡便的做法。如果在Visual Studio工具箱沒看到它,可自行找到Microsoft.Live.Controls.dll加入工具箱(如下圖)。

Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版

不過,有一點要注意,由於在App運作時,不像在ASP.NET範例中會有Callback.aspx負責接入Authorization Code再呼叫API取得Access Token。請參考註冊App一文第3點提及的做法,申請Client ID時,Redirect Domain請留白,然後勾選"Mobile client app"為"Yes",如此,此Client ID即可使用httqs://oauth.live.com/desktop做為Redirect Page,並直接取得Access Token,如此在行動裝置上即可獨立運作。

我想試做的玩具是一個SkyDrive簡易瀏覽器,功能跟上一篇的ASP.NET版差不多,取得使用者對wl.skydrive範圍的同意權後,列出使用者SkyDrive下的目錄,點選目錄可展開列出其下的子項目,若是檔案則可進行檢視,不過WP7能支援的檔案格式比PC少,我隻先選擇對圖片(jpg, png)及影片(wmv, mp3, mp4)提供檢視功能。

MainPage.xaml放入一個SignInButton及ListBox:

排版顯示 純文字

<Grid x:Name="LayoutRoot" Background="Transparent">      
<Grid.RowDefinitions>      
<RowDefinition Height="Auto"/>      
<RowDefinition Height="*"/>      
</Grid.RowDefinitions>      
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">      
Style="{StaticResource PhoneTextNormalStyle}"/>      
Scopes="wl.signin wl.basic wl.skydrive"/>      
</StackPanel>      
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">      
MouseLeftButtonUp="DirList_MouseLeftButtonUp">      
</ListBox>      
</Grid>      
</Grid>      

MainPage.xaml.cs則加入程式邏輯,主要原理是在使用者完成登入及同意後(btnSignIn_SessionChanged事件)建立一個LiveConnectClient,即可使用它呼叫REST API執行各種操作。呼叫結果由client_GetCompleted事件解析,相關的註解說明我附加在程式裡,是以直接看Code:

排版顯示 純文字

using System;      
using System.Collections.Generic;      
using System.Windows.Input;      
using Microsoft.Phone.Controls;      
using Microsoft.Live;      
using Microsoft.Live.Controls;      
namespace MiniSkyDriveExplorer      
{      
public partial class MainPage : PhoneApplicationPage      
{      
// Constructor      
public MainPage()      
{      
InitializeComponent();      
}      
private LiveConnectClient client = null;      
private void btnSignIn_SessionChanged(object sender,       
LiveConnectSessionChangedEventArgs e)      
{      
if (e.Status == LiveConnectSessionStatus.Connected)      
{      
DirItem.LiveSession = e.Session;      
client = new LiveConnectClient(e.Session);      
client.GetCompleted +=       
new EventHandler<LiveOperationCompletedEventArgs>(      
client_GetCompleted);      
//呼叫me/skydrive,取得根目錄folderId      
client.GetAsync("me/skydrive",       
new MyUserState(ApiMethod.SkyDriveProp));      
}      
}      
private const string goUpperSymbol = "\t";      
//所有的REST API呼叫動作完成後,都會觸發此段解析回傳結果      
//為降低範例程式複雜度,以下並未包含防呆容錯的邏輯,實際開發時應補上      
sender, LiveOperationCompletedEventArgs e)      
{      
MyUserState state = e.UserState as MyUserState;      
if (state == null) return;      
switch (state.Method)      
{      
//取得SkyDrive主資料夾的folderId      
case ApiMethod.SkyDriveProp:      
//取出id,列出根目錄下的項目      
ListFiles(e.Result["id"].ToString());      
break;      
//取得目錄清單      
case ApiMethod.SkyDriveDir:      
//由data取得陣列      
List<object> items =      
e.Result["data"] as List<object>;      
if (items != null)      
{      
DirList.Items.Clear();      
DirList.DisplayMemberPath = "DisplayName";      
//加入回上層的邏輯      
if (folderIdStack.Count > 1)      
{      
DirItem di = new DirItem(goUpperSymbol, "[..]");      
DirList.Items.Add(di);      
}      
foreach (Dictionary<string, object> item in items)      
{      
DirItem di  = new DirItem(      
item["id"].ToString(),      
item["name"].ToString()      
);      
//資料夾時額外取得子項目數      
if (di.IsFolder)      
di.Count = int.Parse(item["count"].ToString());      
else //檔案則取得下載網址      
di.SrcUrl = item["source"].ToString();      
//加入清單      
DirList.Items.Add(di);      
}      
}      
break;      
}      
}      
//用以儲存上層目錄的folderId      
private Stack<string> folderIdStack = new Stack<string>();      
private void ListFiles(string folderId)      
{      
if (client == null) return;      
if (folderId == goUpperSymbol)      
{      
folderIdStack.Pop();      
folderId = folderIdStack.Peek();      
}      
else folderIdStack.Push(folderId);      
client.GetAsync(folderId + "/files",       
new MyUserState(ApiMethod.SkyDriveDir));      
}      
private void DirList_MouseLeftButtonUp(object sender,       
MouseButtonEventArgs e)      
{      
DirItem di = DirList.SelectedItem as DirItem;      
//資料夾的話,繼續展開      
if (di.IsFolder || di.Id == goUpperSymbol) ListFiles(di.Id);      
//否則試著下載回來檢視      
else      
{      
DirItem.Current = di; //將此DirItem設為Current      
NavigationService.Navigate(      
new Uri("/Viewer.xaml", UriKind.Relative));      
}      
}      
}      
}      

為了讓程式更結構化一點,我宣告了一個很簡單的SkyDrive項目物件模型: (即上面MainPage.xaml.cs中所出現DirItem、MyUserState物件的由來)

排版顯示 純文字

using System;      
using System.Net;      
using System.Windows;      
using System.Windows.Controls;      
using System.Windows.Documents;      
using System.Windows.Ink;      
using System.Windows.Input;      
using System.Windows.Media;      
using System.Windows.Media.Animation;      
using System.Windows.Shapes;      
using Microsoft.Live;      
namespace MiniSkyDriveExplorer      
{      
#region 用以記錄REST API呼叫資訊的狀態物件      
public enum ApiMethod      
{      
SkyDriveProp, // me/skydrive      
SkyDriveDir,  // folderId/files      
SkyDriveGetImage,      
SkyDriveGetMedia      
}      
public class MyUserState      
{      
public ApiMethod Method;      
public string FileName;      
public MyUserState(ApiMethod method)      
{      
Method = method;      
}      
}      
#endregion      
#region 簡單的目錄物件      
public class DirItem      
{      
//是否為資料夾      
public bool IsFolder      
{      
get { return Id.StartsWith("folder"); }      
}      
//顯示名稱      
public string Name;      
//foderId或fileId      
public string Id;      
//上層資料夾的folderId      
public string ParentId;      
//若為資料夾時,標示其下項目數      
public int Count;      
//下載來源URL      
public string SrcUrl;      
//顯示名稱,資料夾為[folderName](count),檔案則為fileName      
public string DisplayName      
{      
get      
{      
return IsFolder && !Name.Equals("..") ?      
string.Format("[{0}]({1})", Name, Count) : Name;      
}      
}      
public DirItem(string id, string name)      
{      
Id = id;      
Name = name;      
}      
//用此靜態屬性當作頁面間的傳遞媒介      
public static DirItem Current = null;      
//供跨頁面共用LiveConnectSession      
public static LiveConnectSession LiveSession = null;      
}      
#endregion      
}      

除了SkyDrive清單展示,我另外做了一個Viewer.xaml,用來呈現SkyDrive中的圖片(png, jpg)及媒體檔(wmv, mp3, mp4)。 

註: Windows Phone模擬器不支援部分媒體格式,測試前可先看一下MSDN檔案,其中有些已註明This codec is unsupported in Windows Phone Emulator的格式,測試前請認清,以免在模擬器上猛試不成白花時間! (謎之聲: 這條註解怎麼隱含著強烈的怨念?)

要從SkyDrive下載檔案,可以透過LiveConnectClient.DownloadAsync輕鬆達成,在client_DownloadCompleted事件中可以直接取得byte[],十分友善。媒體檔的部分,我還沒試出可以串流播放的做法,是以先採行的方式是先將下載結果存成IsolatedStorageFile,再叫出MediaPlayerLauncher進行播放。程式範例如下:

排版顯示 純文字

using System;      
using System.Linq;      
using System.Windows;      
using System.Windows.Controls;      
using Microsoft.Phone.Controls;      
using Microsoft.Live;      
using System.Windows.Media.Imaging;      
using System.IO.IsolatedStorage;      
using Microsoft.Phone.Tasks;      
namespace MiniSkyDriveExplorer      
{      
public partial class Viewer : PhoneApplicationPage      
{      
public Viewer()      
{      
InitializeComponent();      
}      
private string[] imgExts = "png,jpg".Split(',');      
private string[] mediaExts = "wmv,mp3,mp4".Split(',');      
private LiveConnectClient client = null;      
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)      
{      
if (DirItem.LiveSession == null || DirItem.Current == null)      
return;      
//建立LiveConnectClient      
client = new LiveConnectClient(DirItem.LiveSession);      
client.DownloadCompleted +=       
new EventHandler<LiveDownloadCompletedEventArgs>(      
client_DownloadCompleted);      
//清空內容      
ContentPanel.Children.Clear();      
//取得要檢視的檔案      
DirItem di = DirItem.Current;      
ApplicationTitle.Text = di.Name;      
//由副檔名決定開啟方式      
string ext = System.IO.Path.GetExtension(di.Name).ToLower().TrimStart('.');      
if (imgExts.Contains(ext))      
client.DownloadAsync(di.SrcUrl, new MyUserState(ApiMethod.SkyDriveGetImage));      
else if (mediaExts.Contains(ext))      
//針對媒體檔,比較貼心的方式是透過Streaming方式播放      
//此處隻簡單示範整個檔案下載完畢後才撥放      
client.DownloadAsync(di.SrcUrl,      
new MyUserState(ApiMethod.SkyDriveGetMedia) { FileName = di.Name });      
else      
ContentPanel.Children.Add(new TextBox() { Text = "Unsupported File Type" });      
}      
void client_DownloadCompleted(object sender, LiveDownloadCompletedEventArgs e)      
{      
MyUserState state = e.UserState as MyUserState;      
if (state == null) return;      
switch (state.Method)      
{      
case ApiMethod.SkyDriveGetImage:      
BitmapImage bmp = new BitmapImage();      
bmp.SetSource(e.Result);      
Image img = new Image();      
img.Source = bmp;      
ContentPanel.Children.Add(img);      
break;      
case ApiMethod.SkyDriveGetMedia:      
//此處採用將檔案下載儲存後再播放的做法      
IsolatedStorageFileStream fs =      
IsolatedStorageFile.GetUserStoreForApplication()      
.CreateFile(state.FileName);      
e.Result.CopyTo(fs);      
fs.Close();      
MediaPlayerLauncher mpl = new MediaPlayerLauncher()      
{      
Location = MediaLocationType.Data,      
Media = new Uri(state.FileName, UriKind.Relative)      
};      
mpl.Show();      
break;      
}      
}      
}      
}      

程式實際執行結果如下,按下SignInButton後,畫面會變成Windows Live的登入畫面,使用者輸入Windows Live帳號並同意授權後:

Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版
Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版

程式便可透過me/skydrive REST API取得使用者SkyDrive根目錄的Id,接著使用folderId/files就可取回該目錄下所有子項目的資訊,以ListBox的方式展示出來。

Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版
Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版

檢視圖片及影片的範例:

Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版
Live SDK筆記5-SkyDrive Explorer, Windows Phone 7版

很簡單吧!? 大家一起為WP7 App加入SkyDrive支援吧!