Finally, I decide to release the source code of my Windows Phone Google Reader App.
Please feel free to https://xreadr.codeplex.com/ here.
Here is my project summary:
Project Description
XReadr is a simple Windows Phone 7 app using Google Reader API.
It already has login, browse and mark it as read function here.
XReadr is simple Windows Phone 7 App which using Google Reader API.
It also has follow function:
1. Login page which using HttpWebRequest
2. Browse Unread Labels
3. Browse every news on Label
I also put the first draft of source code, I hope it could help all of you which want to write more Windows News App here.
More implementation detail about how Windows Phone to use Google Reader API, please refer my blog here:
http://www.evanlin.com/blog/archives/001138.html
http://www.evanlin.com/blog/archives/001139.html
It write by Traditional Chinese, feel free to ask me via English.
這幾天開始把原來的RSS Reader APP程式多加頁面後,發現了一些小問題。網路上或許還算是好找。
但是我稍微整理一下,其實Pou’s blog兩篇文章有相當清楚的解釋
在此只把常遇到的兩個問題整理一下:
<Tasks> <DefaultTask Name="_default" NavigationPage="MainPage.xaml" /> </Tasks>
string mylogin = "/Page2.xaml";
mylogin += "?Param1=" + "VALUE1";
mylogin += "&Param2=" + "VALUE2";
if (!String.IsNullOrWhiteSpace(mylogin))
{
this.NavigationService.Navigate(new Uri(mylogin, UriKind.Relative));
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
//Get parameter
string myParam1 = NavigationContext.QueryString["Param1"];
string myParam2 = NavigationContext.QueryString["Param2"];
}
這樣就可以了~~~
參考網頁:
3. 瀏覽全部尚未閱讀的列表
接上篇文章,接下來會開始講解如何去獲得使用者所有標籤(Label)的文章。
一開始首先一樣是先提供這裡會用到的相關Google Reader API的講解
https://www.google.com/reader/api/0/unread-count?allcomments=true&output=json&ck=12121&client=scroll
你就可以在瀏覽器上面獲得你要的資訊。你如果有登入你可能會獲得像是以下的一些資料。
接下來是取得資料的相關原始碼:
public void GetGoogleReaderUnReadCount()
{
string auth_params = string.Format("https://www.google.com/reader/api/0/unread-count?allcomments=true&output=json&ck=" + DateTime.Now.Ticks.ToString() + "&client=scroll");
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(auth_params);
httpRequest.Method = "GET";
httpRequest.Headers["Authorization"] = "GoogleLogin auth=" + UserAuth;
httpRequest.Headers["Cookie"] = "SID=" + UserSid;
httpRequest.BeginGetResponse(new AsyncCallback(ResponseCallbackInner), httpRequest);
}
抓回資料後,就開始要去parse這些資料,這邊會用到大量的JSON相關技巧,我也盡量試著解釋清楚一點給大家知道。首先,會拿到以下的資料,以下是以我的資訊當作範例~跟你拿到的可能會有點差異。
{
"max":1000,
"unreadcounts":[
{
"id":"user/-/label/教學網站",
"count":9,
"newestItemTimestampUsec":"1335595557150290"
},
{
"id":"user/-/label/Funny",
"count":83,
"newestItemTimestampUsec":"1335621983685977"
},
{
"id":"user/-/label/專業評論",
"count":38,
"newestItemTimestampUsec":"1335617137314302"
}
}
這些資料就是JSON的資料,關於JSON是甚麼與JSON.NET該怎麼使用的部分,網路上有許多相關的程式碼,我這裡就不詳述了。
根據上面資料排序的結果,我們需要去抓unreadcounts下面的子結點,而他的資料格式如下:
public class ServerUnreadResult //JSON
{
public string id { get; set; }
public string count { get; set; }
public string newestItemTimestampUsec { get; set; }
}
所以在我們程式碼中,就是可以依照以下的安排:
private void ParseUnreadList(string responseString)
{
//JSON Parse
JObject googleSearch = JObject.Parse(responseString);
// get JSON result objects into a list
IList<JToken> results = googleSearch["unreadcounts"].Children().ToList();
// serialize JSON results into .NET objects
IList<ServerUnreadResult> searchResults = new List<ServerUnreadResult>();
foreach (JToken result in results)
{
ServerUnreadResult searchResult = JsonConvert.DeserializeObject<ServerUnreadResult>(result.ToString());
//Parse the label name
// ex: user/06771113693638414260/label/Win8
string LabelID = searchResult.id;
string[] arrs = LabelID.Split('/');
if (!arrs[0].Contains("user"))
continue;
LabelID = arrs[3];
// Add to Your arrary...
.......
}
}
開始講解這段的程式碼,主要就是第7行的部分可以直接選取到unreadcounts子結點下面所有的內容。然後再針對所有子結點開始DeserializeObject,這樣就可以把所有資料填入到ServerUnreadResult 所產生的類別資料中。這裡主要需要的是標籤名稱與尚未讀取的文章數目。
4. 打開某個標籤(Label)內去看裡面還沒閱讀的文章
當你知道某個標籤內有多少文章,使用者就應該可以點選該標籤去看某個標籤內的所有文章。所以我們需要開始去處理如何查詢某個標籤內所有尚未閱讀的文章。
接下來的部分有點小複雜的是,因為傳回來的JSON資料會相當的多,所以可能需要一些工具去幫助你了解JSON資料。個人推薦http://json.parser.online.fr/這個網站,他可以把你JSON回傳得資料展開。可以幫助你看清楚資料裡面的階層關係。
跟之前的介紹一樣,一開始先提供相關鏈結:
http://www.google.com/reader/api/0/stream/contents/user/" + UserInfoClass.userId + "/label/"+sLabel+"?n=15&ck=" + DateTime.Now.Ticks.ToString() + "&client=scroll&format=json&xt=user/" + UserInfoClass.userId + "/state/com.google/read
跟之前的指令一樣你只要把UserInfoClass.userId換成你的ID還有把DateTime.Now.Ticks.ToString() 換成任何一個隨意的值。你就可以在瀏覽器上面獲得你要的資訊。這裡有一個參數要特別講解。
&xt=user/-/state/com.google/read
這個參數就是幫你把你讀過的部分去除掉,也就是挑選你"尚未閱讀的部分"。
此外另外一個參數n=15 就是顯是你要幾筆資料,你可以將前一個動作所取出來所有尚未讀取的文章放在這裡,預設值是給20。
接下來就是提供給大家相關的程式碼的部分:
public void GetATOMbyLabel(string sLabel)
{
string auth_params = string.Format("http://www.google.com/reader/api/0/stream/contents/user/" + UserInfoClass.userId + "/label/"+sLabel+"?n=15&ck=" + DateTime.Now.Ticks.ToString() + "&client=scroll&format=json&xt=user/" + UserInfoClass.userId + "/state/com.google/read");
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(auth_params);
httpRequest.Method = "GET";
httpRequest.Headers["Authorization"] = "GoogleLogin auth=" + UserAuth;
httpRequest.Headers["Cookie"] = "SID=" + UserSid;
httpRequest.BeginGetResponse(new AsyncCallback(ResponseCallback), httpRequest);
}
這邊沒有太多程式碼的部分需要說明。
接下來就要處理回傳的JSON資料,這僅僅是一筆的資料如下,由於資料有點大量,我盡量刪除不需要的部分:
{
"direction":"ltr",
"id":"user/-/label/Apple",
"title":"在 Google 閱讀器中取得 Evan Lin 的「Apple」",
"continuation":"COGG3sTqta8C",
"self":[
{
"href":"http://www.google.com/reader/api/0/stream/contents/user/-/label/Apple?n\u003d15\u0026ck\u003d2323232323\u0026client\u003dscroll\u0026format\u003djson"
}
],
"author":"Evan Lin",
"updated":1334555570,
"items":[
{
"id":"tag:google.com,2005:reader/item/f1c6c6aed52d2682",
"title":"【限時免費】Moco Cam:復古底片當道,LOMO 鏡頭隨你拍,超過20種濾鏡效果,原價0.99,限時免費下載中",
"alternate":[
{
"href":"http://funiphone.pixnet.net/blog/post/37297964",
"type":"text/html"
}
],
"content":{
"direction":"ltr",
"content":"\u003cp\u003e\u003cimg src\u003d\"http://pic.pimg.tw/funiphone/1334549031-3221087701_q.png\" alt\u003d\"\" width\u003d\"150\"\u003e\u003cimg src\u003d\"http://pic.pimg.tw/funiphone/1334549497-4194937028_n.png\" alt\u003d\"\" width\u003d\"450\"\u003e\u003c/p\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003cp\u003e\u003ca href\u003d\"http://itunes.apple.com/app/moco-cam-camera-lens-effects/id371271476?mt\u003d8\"\u003e\u003cimg style\u003d\"display:block;margin-left:auto;margin-right:auto\" src\u003d\"http://pic.pimg.tw/funiphone/1310632558-92007661eba0bc5e489df3d66cfff9b9.jpg?v\u003d1310632559\" alt\u003d\"按此下載.jpg\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src\u003d\"http://a1.mzstatic.com/us/r1000/059/Purple/b6/38/6b/mzl.fgzqmjnx.320x480-75.jpg\" alt\u003d\"iPhone Screenshot 1\" width\u003d\"280\"\u003e \u003cimg src\u003d\"http://a5.mzstatic.com/us/r1000/038/Purple/ff/8a/7c/mzl.fzmsyokn.320x480-75.jpg\" alt\u003d\"iPhone Screenshot 3\" width\u003d\"280\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:14pt\"\u003e\u003cspan\u003e【目前售價:\u003cspan style\u003d\"color:#ff0000\"\u003e限時免費\u003c/span\u003e\u003c/span\u003e\u003cspan\u003e】\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:14pt\"\u003e【檔案大小:5.0 MB】\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large\"\u003eMoco Cam (Camera Lens Effects) 限時免費的好用鏡頭濾鏡軟體,這個App介面十分簡單,但支援超過20幾種濾鏡。原價0.99美金,限時免費中,喜歡用iPhone、iPad等iOS裝置照相拍照的話,可以趁限免時下載此程式App。\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large\"\u003e(下為 iTunes 上的玩家評價)\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large\"\u003e\u003cimg title\u003d\"89\" src\u003d\"http://pic.pimg.tw/funiphone/1334549972-1765202292.png\" alt\u003d\"89\" border\u003d\"0\"\u003e \u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large;color:#0000ff\"\u003eFun iPhone(APP玩家)評分:★★★\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large\"\u003e\u003cbr\u003e\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large\"\u003e此相機修圖程式的首頁介面非常簡單,其實也有點陽春。按下方咖啡色方塊可進入程式。\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large\"\u003e\u003cimg src\u003d\"http://a5.mzstatic.com/us/r1000/030/Purple/29/f7/f2/mzl.xxifelxh.320x480-75.jpg\" alt\u003d\"\" width\u003d\"320\"\u003e\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan style\u003d\"font-size:large\"\u003e要選擇 take a photo 相機照相或直接從手機相簿中選擇想加入濾鏡的圖片\u003c/span\u003e\u003c/p\u003e \u003cdiv\u003e\u003ca href\u003d\"http://funiphone.pixnet.net/blog/post/37297964\"\u003e(繼續閱讀...)\u003c/a\u003e\u003cimg src\u003d\"http://pixanalytics.com/pa.gif?t\u003dfront_blog_feed\u0026amp;document.URL\u003dhttp://funiphone.pixnet.net/blog/post/37297964\"\u003e\u003c/div\u003e"
},
"origin":{
"streamId":"feed/http://funiphone.pixnet.net/blog/feed/rss",
"title":"Fun iPhone 愛瘋玩家 (APP玩家):: 痞客邦 PIXNET ::",
"htmlUrl":"http://funiphone.pixnet.net/blog"
}
}
}
這邊資料有點多,我盡量去分開講。這裡大家會用到一些資料內容主要有以下的資料。
提供程式碼之前,一樣的我們需要把資料先宣告成類別如下:
public class ServerLabelATOMResult //JSON
{
public string id { get; set; }
public string title { get; set; }
}
public class ServerLabelATOMResult2 //JSON
{
public string href { get; set; }
public string type { get; set; }
//public string content { get; set; }
}
接下來就提供程式碼的部分:
private void ParseLabelATOM(string responseString)
{
//JSON Parse
JObject googleSearch = JObject.Parse(responseString);
// get JSON result objects into a list
IList<JToken> results = googleSearch["items"].Children().ToList();
// serialize JSON results into .NET objects
IList<ServerLabelATOMResult> searchResults = new List<ServerLabelATOMResult>();
foreach (JToken result in results)
{
ServerLabelATOMResult searchResult = JsonConvert.DeserializeObject<ServerLabelATOMResult>(result.ToString());
string newsID = searchResult.id;
string feedID = result["origin"]["streamId"].ToString();
string feedTitle = result["origin"]["title"].ToString();
///// Get the link of this news.
string sLink = "";
IList<JToken> results2 = result["alternate"].Children().ToList();
foreach (JToken result2s in results2)
sLink = result2s["href"].ToString();
//Add to your data set here
}
}
這裡要特別講解一下,由於JSON裡面的LIST 主要是處理多個資料集(LIST)的方式,就如同對應到JSON的
":["
所以在這裡的["alternate"]["href"]必須當作是多個資料,雖然Google本身也只有包了一個資料集而已,也就是說雖然Google裡面只有"href"與"http"共一個資料集(LIST)但是我們還是得這樣處理他,不然拿不到資料。這個部分也弄了很久,在此分享給大家。
5. 打開該文章網站並且將其標記已經閱讀(mark as read)
這邊的部分就請參照我之前的文章,在這裡就不再詳述了。
這個禮拜初,最後總算弄好標記閱讀(Mark Read)後,Google Reader App總算也完成了。不過由於接下來可能會把一些時間拿去研究iOS。最後決定把這部份的研究做一個總整理。這一篇文章會包括以下的技術:
關於RSS Reader App:
首先我想在這裡先稍微整理一下,一個簡單的RSS Reader的Windows Phone APP是相當簡單的。其實只要會簡單的Webclient加上去使用內建的資料格式就可以完成,這裡微軟也提供完整的原始碼與介紹。本篇介紹的部分主要都是專注在Google Reader的部分。
Google Reader API:
由於我是在Windows Phone上面做開發(Windows Phone 7.1)所以無法直接使用Google Data上面的相關API,在這裡也要特別解釋,如果你只是WPF或是一般Windows程式~ 你可以直接使用Google Data而不需要一步步的完成Google Reader API的部分,在這裡把之前文章列過的Google Reader API整理一次:
一個簡單Google Reader APP的流程:
接下來我會用碼個Google Reader 會使用到的流程來介紹該如何去撰寫,首先一個Google Reader APP會有以細的流程:
接下來我會針對以下的部分,開始整理一些原始碼:
1. 登入,也就是登入你的Google Reader 帳號
在這裡會使用到基本的Google 登入部分,你可以直接在你的瀏覽器上頭打入以下的鏈結。
https://www.google.com/reader/api/0/token?ck=212121212&client=scroll
稍微解釋一下參數:
ck: 就是時間標記(timestamp),你可以用DateTime.Now.Ticks.ToString()拿到
client:先填入scroll的方式
如果你已經登入過的話,一樣會有一樣的結果
public void LoginGoogleReader()
{
string email = "YOUR_EMAIL";
string passwd = "YOUR_PASSWORD";
string auth_params = string.Format("https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email=" + email + "&Passwd=" + passwd + "&service=reader&source=J-MyReader-1.0");
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(auth_params);
<font color="#ff0000"> httpRequest.Method = "POST";</font>
RequestState myRequestState = new RequestState();
myRequestState.request = httpRequest;
IAsyncResult result =
(IAsyncResult)httpRequest.BeginGetResponse(new AsyncCallback(AuthRespCallback), myRequestState);
}
但是你也需要去把登入回來的SID與AuthID去parse出來,以下提供相關方法:
private void ParseLogin(string responseString)
{
string[] arrs = responseString.Split('\n');
foreach (string arr in arrs)
{
//Such as follows, but we only need Auth and SID
//SID=DQAAALcAAACH5nzd2xVxumGUJGjoTT9DblBfAuI1Sj-6Evtu_91q8Fkbis_S1qv66-vatdUe9HMqqELN3AijrD-CiQyzqJtr_XgTtJIDnfDeVmoGjtsSpkyIB4sES5slrOkTvRJd67dG0tlwTih8btejcPqAVYCJMD6f-a-x3PHa2JQ-Ehsh-rtjNzW6y7Riqosyawffvg4DPbtvUJFsIsum8d32EQ_XuxmUM7NFuhwW5OG8aw3lLO_jO0fLaznl7n5DfCC61KSA\n
//LSID=DQAAALkAAAAAeAUiJHtt2vL41E3bfMAJwE_waG3qEtAgEsv-Il0xXznDCdm-Z_jQZ4Log9DbGqTMd1-t08udWBJWeQ9VDG0uOC4H5nB0zJ_WGv1E17v3EVeveemKvpu9eN2YBkQlr6hMtZlZWyAb5w0uwAx6kPdXnnuuYC4o0RHv2em0CrOAFzpNYZvLOhuB_veFZ9bsnPy6GP0_HHQGe2o3dJsoJK_DKyq85QteslDzcQySldfwNGUy46Q4HLKhZZPDrjnO_eU\n
//Auth=DQAAALgAAAAAeAUiJHtt2vL41E3bfMAJta7kSZRtYIzGfm8uJU_jVFjmIFbYYL9WaLS7Xj3xqdwLOrzrBipqL8ItZks4Hf71NY2yTyZnAIG5ysrlA9kCcoZGDDqo3ib9avvgC4pPwXB2uQ3rBYt0gqYs28DkEX6fDD4S3j_NwBESynhOUhcTKqhN3pYX1VfH6uU4285yV7O3w7NKfF8kkTOEFl5toztOwnA4JWnbC5Rjb_gMXKmKnzayTMevgO_XfGWqqNa8x5M\n" string
string[] tmp = arr.Split('=');
if (tmp[0] == "Auth")
{
UserAuth = tmp[1];
}
else if (tmp[0] == "SID")
{
UserSid = tmp[1];
}
}
}
記得把UserAuth與UserSid要儲存下來,之後每一個動作都需要。
2. 取得登入者的相關資訊
登入以後我們需要去取得使用者的ID,這樣主要也是給我們去找尋標籤(Label)或是做一些使用者的相關功能來使用。
如同之前一樣,你也可以在你的瀏覽器上面打入以下的鏈結來看結果:
https://www.google.com/reader/api/0/user-info?format=json&ck=121212122&client=scroll
這裡多了一個參數,也就是JSON的輸出格式
format: 如果不是寫joson會傳回XML資料
你打出來的使用者資訊應該會像以下的資料一樣:
{
"userId":"XXX",
"userName":"XXX",
"userProfileId":"XXX",
"userEmail":"XXX@gmail.com",
"isBloggerUser":true,
"signupTimeSec":1188451343,
"publicUserName":"XXX",
"isMultiLoginEnabled":true
}
而我們需要的也只有userProfileId,接下來先給大家如何去抓取ID的程式碼:
public void GetUserInformation()
{
string auth_params = string.Format("https://www.google.com/reader/api/0/user-info?format=joson&ck=" + DateTime.Now.Ticks.ToString() + "&client=scroll");
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(auth_params);
httpRequest.Method = "GET";
httpRequest.Headers["Authorization"] = "GoogleLogin auth=" + UserAuth;
httpRequest.Headers["Cookie"] = "SID=" + UserSid;
httpRequest.BeginGetResponse(new AsyncCallback(ResponseCallbackInner), httpRequest);
}
在這裡會使用到之前抓的兩個參數UserAuth以及UserSid記得要填到Header之內不然都會得到error 400。
如果正確的拿到資料的話,我也提供利用JSON來抓取資料的方式。
public class ServerUserInfo //JSON
{
public string userId { get; set; }
public string userName { get; set; }
public string userProfileId { get; set; }
public string userEmail { get; set; }
public string isBloggerUser { get; set; }
public string signupTimeSec { get; set; }
public string publicUserName { get; set; }
public string isMultiLoginEnabled { get; set; }
}
private void ParseUserInfo(string responseString)
{
UserInfoClass = JsonConvert.DeserializeObject<ServerUserInfo>(responseString);
staUserID = UserInfoClass.userId;
}
看到了嗎?這就是JSON神奇的地方,你只要一行程式碼就可以去把所有資料提到你的資料UserInfoClass(是class ServerUserInfo 產生的)中。
先寫到這個部分,接下來就是瀏覽標籤(Label)與找到文章內容的部分…
上一篇文章有講解利用HttpWebRequest的方式來完成Google Reader中的標記閱讀,但是因為edit-tag本身就不屬於太消耗時間的動作。與其去用HttpWebRequest的非同步的方式,不如使用容易又好用的WebClient,所以我就把我原來程式碼做了一點修改。
public void MarkArticleAsRead()
{
CurrentTransType = Transaction_Type.MARK_AS_READ;
string auth_params = string.Format("https://www.google.com/reader/api/0/edit-tag?client=scroll&format=joson&ck=" + DateTime.Now.Ticks.ToString());
string postData = "";
postData += "&i=tag:google.com,2005:reader/item/fa42c976c848ecf4";
postData += "&a=user/-/state/com.google/read";
postData += "&s=feed/http://funiphone.pixnet.net/blog/feed/rss";
// Note the token must get within 30 mins
postData += "&T=//mUESPUMtDyZh6BaFXd-CqQ";
WebClient wc = new WebClient();
wc.Headers["Content-type"] = "application/x-www-form-urlencoded";
wc.Headers["Authorization"] = "GoogleLogin auth=" + UserAuth;
wc.Headers["Cookie"] = "SID=" + UserSid;
try
{
wc.UploadStringAsync(new Uri(auth_params), "POST", postData);
}
catch (WebException e)
{
//handle error if any
}
}
幾個需要注意的地方如下:
雖然網路上沒有完整使用C#與Windows Phone 7.1的範例,不過還是盡量用中文來寫這篇吧。其實網路上英文的資料應該也不少,不論是pyrfeed這裡的Google API列表,還是Unofficial Google Reader API或是R2 google reader on Google code。不過要完整的C#還有Windows Phone 7.1的sample code我也是想辦法K了半天這三份API文件然後不斷的測試,在這裡就列出一些片段的程式碼。
public void MarkArticleAsRead(string newID)
{
string auth_params = string.Format("https://www.google.com/reader/api/0/edit-tag?client=scroll&format=joson&ck=" + DateTime.Now.Ticks.ToString());HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(auth_params);
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
httpRequest.Headers["Authorization"] = "GoogleLogin auth=" + UserAuth;
httpRequest.Headers["Cookie"] = "SID=" + UserSid;
httpRequest.BeginGetRequestStream(new AsyncCallback(GetPostRequestStreamCallback), httpRequest);
}
不過這裡有一些需要注意的是SID與auth ID需要另外區擷取,這在我其他文章提到。不過接下來就是重點的部分。也就是post data該如何填、該放哪些資料。
private static void GetPostRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
Stream postStream = request.EndGetRequestStream(asynchronousResult);
string postData = "";
postData += "&i=tag:google.com,2005:reader/item/fa42c976c848ecf4";
postData += "&a=user/-/state/com.google/read";
postData += "&s=feed/http://funiphone.pixnet.net/blog/feed/rss";
// Note the token must get within 30 mins
postData += "&T=//mUESPUMtDyZh6BaFXd-CqQ" + LoginToken;
// Convert the string into a byte array.
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Write to the request stream.
postStream.Write(byteArray, 0, postData.Length);
postStream.Close();
// Start the asynchronous operation to get the response
request.BeginGetResponse(new AsyncCallback(GetPostResponseCallback), request);
}
在這裡也要注意的是Token需要再30分鐘之內取得~ 建議是呼叫之前馬上去取得token來使用。還有這裡的參數”a=”user 之後有user id 需要填,在這裡就不列出來。 &i=要填的是news 的id,還有&s=要填的是rss ID,你可以在另外一個參數”http://www.google.com/reader/api/0/stream/contents/user” 去取得。最後是結果的部分:
private static void GetPostResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
// "responseString" should be "OK" if post succucess
// Close the stream object
streamResponse.Close();
streamRead.Close();
response.Close();
}
這裡重要的是~ 如果這篇文章被mark as read你就會得到OK,但是如果之前已經mark as read你就會得到一個exception,這是需要注意的。
目前先寫到這裡~ 找時間把全部的流程跑一次~ 順便講解一下JSON的部分。
本篇文章應該會用中文撰寫到完,接續著我前幾篇的文章,我一共寫了幾篇跟Google Reader溝通有關的文章。也讓我來仔細敘述當初為何會有這些文章的出現吧。
大概是兩個月前XDite大大在plurk po了一篇 "我要在一個月之內把Reeder幹出來" (大概是這樣吧!雖然我忘記原文了,而且大大好像後來也刪除了)。其實那一篇我看了以後很震撼~ 對喔!! 可以利用完全仿製另外一個商用APP的方式來練功。
於是我也開始撰寫APP,由於我自己只有iPhone,但是卻沒有任何MAC的電腦可以給我提供開發的環境,也只好拿公司最容易找到工具。Microsoft的 Visual Studio來撰寫Windows Phone。
一路上從四月才開始把專案建起來到現在,從完全不會寫Windows Phone到搞定Data Binding、知道如何使用Lambda與Delegate、也慢慢把Windows Phone原件搞懂,當然好久沒摸的WPF也開始拿回來K了一陣,到最後的LINQ、JSON(這個方便應該之後會寫一篇文章來做個說明)。其實寫好一個Google Reader App能夠學習的APP技巧真的不少,在這裡也稍微列一下:
到目前為止利用自己上班剩餘時間,大概忙了三個禮拜的結果,目前已經可以登入與看文章。由於在今天好不容易把Google Reader裡面的設定閱讀(mark it as read)也搞定了。 現在也才可以寫篇文章做點記錄。不過接下來才是Reeder令人激賞的好用地方,我也先把自己的TODO 列一下:
好啦!! 今天也總算把基本功能都完成了! 繼續努力了~~~
SyntaxHighlighter是一個在網站上相當好用的東西。我終於也在www.evanlin.com\blog把他安裝好了
不過呢~ 由於Window Live Writer 還沒有找到真的讓我覺得很好用的部分
這個Plugin 不好用(更正: 因為不支援最新版本的Syntaxhighlighter 3.0.8.3)
以下是我加入到MT 模板的code(不過這是用VSpaste)先貼的
<!-- Include required JS files --> <script type="text/javascript" src="js/shCore.js"></script> <script type="text/javascript" src="js/shBrushJScript.js"></script> <script type="text/javascript" src="js/shBrushCSharp.js"></script> <script type="text/javascript" src="js/shBrushCss.js"></script> <script type="text/javascript" src="js/shBrushJava.js"></script> <script type="text/javascript" src="js/shBrushXml.js"></script><
link href="css/shCore.css" rel="stylesheet" type="text/css" />
<link href="css/shThemeDefault.css" rel="stylesheet" type="text/css" />
<!-- Include required JS files --><script type="text/javascript">
SyntaxHighlighter.all()
</script>
話說~ VSpaste 這個plugin 也很好用~ 跨Blog又不用安裝~
不過可能支援語系會少一點
以下是使用這個Windows Live Writer Plugin(Windows Live Writer Source Code plugin for SyntaxHighlighter) , 而且也支援最新版本的syntaxhighlighter 3.0.8.3
<!-- Include required JS files -->
<script type="text/javascript" src="js/shCore.js"></script>
<script type="text/javascript" src="js/shBrushJScript.js"></script>
<script type="text/javascript" src="js/shBrushCSharp.js"></script>
<script type="text/javascript" src="js/shBrushCss.js"></script>
<script type="text/javascript" src="js/shBrushJava.js"></script>
<script type="text/javascript" src="js/shBrushXml.js"></script>
<link href="css/shCore.css" rel="stylesheet" type="text/css" />
<link href="css/shThemeDefault.css" rel="stylesheet" type="text/css" />
<!-- Include required JS files -->
<script type="text/javascript">
SyntaxHighlighter.all()
</script>
Google data is good API tool for C# or other language to connect with Google related service, however here is no official Google Reader API on document. and Google data don’t support for Windows phone (only for Windows mobile for now).
However it could be found on web for some of unofficial document for Google Reader API as follows:
But however to come out detail implement for Windows Phone, it quit need time to work detail. Allow me try to list some major problems of this.
The remote server returned an error: NotFound
It is most common error code your will find during you try to login or get any feedback on Window Phone. It could be either you missing your GET parameter or you missing your headers.
Here is some sample code related Login Google Reader and get list from this login. Some more detail communication code about HttpWebRequest, please check http://msdn.microsoft.com/zh-tw/library/system.net.webrequest.begingetrequeststream(v=vs.80).aspx
Something need to note before enter detail code.(check red part)
public void LoginGoogleReader()
{
string email = "YOUR_EMAIL";
string passwd = "YOUR_PASSWORD";
string auth_params = string.Format("https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email=" + email + "&Passwd=" + passwd + "&service=reader&source=J-MyReader-1.0");
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(auth_params);
httpRequest.Method = "POST";
RequestState myRequestState = new RequestState();
myRequestState.request = httpRequest;
IAsyncResult result =
(IAsyncResult)httpRequest.BeginGetResponse(new AsyncCallback(AuthRespCallback), myRequestState);
}
public void GetGoogleReaderPreferenceList()
{
string auth_params = string.Format("https://www.google.com/reader/api/0/preference/list?client=scroll&ck=1293954081436");
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(auth_params);
httpRequest.Method = "GET";
httpRequest.Headers["Authorization"] = "GoogleLogin auth=" + UserAuth;
httpRequest.Headers["Cookie"] = "SID=" + UserSid;
RequestState myRequestState = new RequestState();
myRequestState.request = httpRequest;
IAsyncResult result =
(IAsyncResult)httpRequest.BeginGetResponse(new AsyncCallback(AuthRespCallback), myRequestState);
}
網路新知
/ 學習文件
/ VC6相關問題
/ 關於WindowsPhone的開發研究文章
According to Metro Application Lifetime sample and related document, it description about Metro app about suspend/resume.
However, I also find I could not make my Metro app enter suspend mode, so I try to figure out some answer from MSFT forum. Refer here and here.
Here is my summary for this: