Android C2DM (四):實作之Android篇

25 March 2012

這篇是舊版的,已經被deprecated,新版為GCM,新版的實作請看
新版的Android篇




這篇主要在說明Android端的實作
第一篇在講參數說明
第二篇在講流程說明
第三篇在講server端的實作


這篇主要會完成下列項目:
  1. 基本的manifest設定(加入receiver,permission)。
  2. 向C2DM 註冊。
  3. 接收自C2DM Sever回傳的registration id 並傳送 Registration Id 到我們的Server,以及接收Message


【1.  設定Manifest資料 】 
這步驟,分了兩個部分!
第一個就是要先加入一個receiver,
記得下面的category android:name,
裡面的value要改成自己的package name
如下:
<!-- 用來接收C2DM Server的receiver,且要有send的permssion -->
<receiver android:name=".C2DMReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<!-- Receive the actual message -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="net.kenyang.android" />
</intent-filter>

<!-- Receive the registration id -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="net.kenyang.android" />
</intent-filter>
</receiver>



第二部分就是加入應有的permission,
<!-- 下面的net.kenyang記得要改成自己的package name -->
<permission android:name="net.kenyang.android.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="net.kenyang.android.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.INTERNET"/>



【2.  向C2DM註冊 】
 

這一步驟就是啟動C2DM,並告訴C2DM要註冊,
Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
registrationIntent.putExtra("app", PendingIntent.getBroadcast(SelectLoginMode.this, 0, new Intent(), 0)); // boilerplate
registrationIntent.putExtra("sender", "xxxxx@gmail.com"); // 記得這邊要填入當初你在第三篇註冊的MAIL
startService(registrationIntent);


【3.  接收與傳送資料 】 

這一步驟就是寫一個receiver,
這個receiver會

  1. 接收C2DM傳來的registration id,
  2. 也會接收C2DM傳來的message(這個message就是我們Server傳給C2DM的message)
  3. 並且將registration id傳送到我們第三篇實作的server上面去


我們先在receiver中寫一個函數將registration id傳送至我們第三篇實作的server上面去,
server只要收到,就會傳送資料到C2DM上!
private void fnRegistrationDone(final String strRegistrationId){
try{
Thread thread = new Thread(new Runnable() {

@Override
public void run() {
HttpURLConnection conn = (HttpURLConnection) new URL("http://c2dm.kenyang.net/registration").openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("GET"); // 因為在GAE上面的servlet,我是用get method
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", strRegistrationId.getBytes().length); // 告訴c2dm要傳送的資料長度

// 送出資料
OutputStream out = conn.getOutputStream();
out.write(strRegistrationId.getBytes());
out.close();

conn.fnClose();
// 到這,一個註冊就完成,接著就是等著收資料

}
});
thread.start();

}catch (Exception e) {
}

}



接著開始寫接收資料(registration_id, message)的code,如下:
@Override
public void onReceive(Context context, Intent intent) {

// 接收到C2DM的訊息
if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {

String strRegistrationId = intent.getStringExtra("registration_id");
// 如果註冊失敗
if (intent.getStringExtra("error") != null) {

Log.d("Ken.yang", "c2dm registration fail");

// Registration 成功
} else if (strRegistrationId != null) {
// 把registration_id傳到我們server(呼叫我們上面寫的函數)
fnRegistrationDone(strRegistrationId);

}

} else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {

// 接收來自C2DM的MESSAGE,這message就是我們server傳給C2DM的 (可以看第三篇)
if (intent.getExtras().containsKey("keyHello")){
strValue = intent.getExtras().getString("keyHello");
Log.d("Ken.yang", "C2DM=== receive" + strValue);
}
}
}



完成上面步驟,就完成c2dm了!
也可以收到訊息了!!!!
因為我們第三篇server上傳送的訊息是fromKen,
所以這裡收到的也是fromKen!!




















read more »


Android C2DM (三):實作之Sever篇

23 March 2012

這篇是舊版的,已經被deprecated,新版為GCM,新版的實作請看
新版的Server篇





最後一篇進入到了Android C2DM的coding了!!
哈! 這篇存在草稿也三個月了..........
今天終於有一點點時間來修改!


第一篇在講參數說明
第二篇在講流程說明
這篇專注於server端的實作
下一篇才會專注於android端的實作




OK,
在coding之前,請先去Google官網註冊你的app
比較要注意的是下面兩項
  1. Package name of your Android app *
  2. Role (sender) account email *
第1項,是指你的Android app的package name,這裡一定要填對,不然C2DM Server不知道送到哪個app去。
第2項,是指用來傳遞訊息的帳號,也就是開發者的帳號,這裡的建議是不要用個人帳號,建議新申請另一個帳號來使用。

完成上面註冊動作以後,就可以開始coding了!
網路上的code教學都是拿Google官網上面放的範例(JumpNote)
我覺得這樣不太native,畢竟這是別人寫的app!
所以我直接講最基本的流程!


接下來的code大概會分成下面幾種:

  1. 實作Server端:使用Google Client Login API取得Auth token,並且存起來。
  2. 實作Server端:Sending Message to C2DM Server。
  3. Android端:基本的設定。
  4. Android端:向C2DM 註冊,並且取得registration id。
  5. Android端:傳送 Registration Id 到我們的Server

因為其實有點多,所以就分成兩篇來打,
一篇為Sever篇,就是此篇!
另一篇為Android篇,就是下篇!

【1.  使用Google Client Login API取得Auth token 】

因為得把token傳至C2DM Server,
C2DM Sever才知道你有登入,且是合格的。

至於Google Client Login API的流程在這就不贅述,
就直接實做! 但大家可以去Google Client Login API官網看!!!

這裡我是架在Google App Engine上,把GAE當作我們的Sever。
由於auth token可以重複使用,
所以我們可能可以每段時間去執行ClientLogin,
並取得auth token!
這時候就可以用GAE的cron,可能設定每天去登入一次以取得token。

而Client Login的code如下:
String strClientLoginUrl = "https://www.google.com/accounts/ClientLogin"; // client login的url
String strAccountType = "accountType=HOSTED_OR_GOOGLE&";// 你要登入的帳號型態,這裡就輸入HOSTED_OR_GOOGLE
String strEmail = "Email=test@gmail.com&"; // 你的email!!
String strPasswd= "Passwd=123456&"; // 你的密碼
String strService= "service=ac2dm&"; // 你登入以後,要拿這token存取Google哪個Service,這裡是ac2dm
String strSource= "source=KenYang-AndroidC2DM-1.0";// 這個只是用來identify你的application,格式要companyName-applicationName-versionID


HttpURLConnection conn = (HttpURLConnection) new URL(strClientLoginUrl).openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST"); // 設定request Method為POST
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");


// 送出資料
OutputStream out = conn.getOutputStream();
String strPostData = strAccountType+strEmail+strPasswd+strService+strSource;
out.write(strPostData.getBytes());
out.close();

// 取得response code,成功的話就是200
resp.getWriter().println(conn.getResponseCode()+ "</br>");

// 把資料讀進來並且印出來
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8"));
String strLine = "";
while((strLine=reader.readLine())!=null){
resp.getWriter().println(strLine+"</br>");
}




正常來說,如果你帳號沒有問題,
或者密碼沒有輸入錯誤,
或者的確有這帳號存在!
那麼response code會是200!
就代表登入成功!
此時就要取得auth token。

印出來的資料如下:
SID=DQAAAGgA...7Zg8CTN
LSID=DQAAAGsA...lk8BBbG
Auth=DQAAAGgA...dk3fA5N

這時候,只要取得最後那一行資料即可!
就是Auth那一行!!!
然後把Auth存起來!
這裡我是先存在GAE的datastore!
以利待會使用!




【2.  傳送資料至C2DM Server 】

至於傳送甚麼資料,可以來這官網看詳細參數說明!
這邊就簡單地說明幾種"必需"的參數!

  1. registration_id :android 裝置的id,一定要傳這資料給C2DM Server,C2DM才知道訊息要傳給誰,至於這個值,是來自android device的!
  2. collapse_key :這個value可以是隨意的值,這個參數的用意是怕android device的狀態是離線狀態,一旦狀態恢復為連線,避免使用者突然收到大量訊息!(這個參數可以每次都不一樣,也可以每次都一樣)
  3. data.<key> :這個參數就是你要傳給android device的訊息了! 型態為key-value。如data.keyName=value
  4. GoogleLogin auth :這個不是要post的參數,而是要送出的header值! 而值就是上面存在datastore裡面的Auth!



實際的code如下:
String strSendUrl = "https://android.apis.google.com/c2dm/send";  // c2dm的url
String strRegistrationId = "registration_id="+req.getParameter("device_token")+"&"; // 接收來自android傳來的參數
String strCollapse= "collapse_key=1&"; // 這個參數上面說明了
String strAuth = "xxxxxx"; // 從datastore撈出來的值,這裡就不寫有關datastore的操作了
String strData = "data.keyHello=fromKen"; // 你要送的訊息

String strPostData = strRegistrationId+strCollapse+strData;

HttpURLConnection conn = (HttpURLConnection) new URL(strSendUrl).openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Authorization", "GoogleLogin auth=" + strAuth); // 設定header
conn.setRequestProperty("Content-Length", Integer.toString(strPostData.getBytes().length)); // 告訴c2dm要傳送的資料長度

// 送出資料
OutputStream out = conn.getOutputStream();
out.write(strPostData.getBytes());
out.close();

// 看response code, 200為OK
resp.getWriter().println(conn.getResponseCode()+ "</br></br>");





完成上面的步驟,server端就完成了!
但還要完成android端,才能真正收到訊息!
下一篇會專注於android端的實作






read more »


C# 控制 Webcam 【using Emgu】

04 March 2012

這篇似乎是我歷年來第一篇有關M$的筆記.........
真是寫得有點心不甘情不願XD
畢竟本人真的是沒有很喜歡M$的東西,
當然不是因為M$不好,M$也有它好的一面,
只能說或許是習慣問題....

OK,進入正題,
這篇主要是去說明如何利用C#去控制你的網路攝影機,
且是使用Emgu這套dll,
甚麼是Emgu呢?
他和OpenCV有點關聯,
OpenCV相信不管是Java, C, C++等language的開發者,都了解OpenCV是甚麼!?
簡單說,OpenCV是一套強大的影像處理library,由INTEL開發,
非常強大,甚至你可以利用OpenCV去做到OCR,很方便。
也由於OpenCV沒有支援C#,那C#要怎麼使用OpenCV呢?
就是靠EmguEmgu是一套允許OpenCV的function在C#等語言中被使用。

但我們這邊並不會使用到OpenCV的功能,
就是簡單的介紹Emgu很基本的功能,
就是存取攝影機,
首先先去下載Emgu,並且安裝
安裝完成以後就可以開始寫程式....

看你是要開啟一個WPF專案,還是一個Window Form專案,
建議你開啟Window Form,因為到時Webcam回傳回來的image型態,
可以直接在 Window Form中的pictureBox裡面使用。

開啟以後,就在你的專案中加入reference,
加入以下四個dll,dll的位置就是在你安裝Emgu位置的bin底下:

  1. Emgu.CV.dll
  2. Emgu.CV.ML.dll
  3. Emgu.CV.UI.dll
  4. Emgu.Util.dll

加入以後,請先儲存你的專案,
儲存以後請在你安裝Emgu位置的bin底下找到兩個dll,

  1. opencv_core231.dll
  2. opencv_highgui231.dll

把這兩個dll放置到你的專案的/bin/Debug/底下。
因為Emgu.CV.dll會使用到上述兩個dll。


完成上述動作以後就開始寫code,
先import會使用到的lib,如下:
using Emgu.CV;
using Emgu.CV.Structure;


先宣告一個Capture物件,如下:
private Capture cap = null;                 // Webcam物件
這個物件就是用來連結到你的webcam。


接著在Form1_Load event中,
連結到攝影機以及建立一個event用來抓取畫面,如下:
private void Form1_Load(object sender, EventArgs e)
{
cap = new Capture(0); // 連結到攝影機0,如果你有兩台攝影機,第二台就是1
Application.Idle += new EventHandler(Application_Idle); // 在Idle的event下,把畫面設定到pictureBox上(當然你也可以用timer事件)
}


接下來要寫抓取畫面event的code,
        void Application_Idle(object sender, EventArgs e)
{
Image<Bgr, Byte> frame = cap.QueryFrame(); // 去query該畫面
pictureBox1.Image = frame.ToBitmap(); // 把畫面轉換成bitmap型態,在餵給pictureBox元件
}


完成以後按下F5執行,應該就可以順利取得攝影機畫面瞜!



read more »