顯示具有 Android 標籤的文章。 顯示所有文章
顯示具有 Android 標籤的文章。 顯示所有文章

2013年6月9日 星期日

Android Studio import ActionBarSherlock(2)

上次寫了一個範例,發現在Android Studio的import分兩種一種是對程式

一個是對整個project,要兩個都引入才算真的import!!

這次就來真正完整教學!!


這次就略過開新專案的過程直接進到主題

專案名稱: MyApplicationProject

首先先 Actionbarsherlock 下載 Lib下來

Github:   https://github.com/JakeWharton/ActionBarSherlock
Website: http://actionbarsherlock.com/

接著再裡面找他的Lib /actionbarsherlock

然後再把它拉到你的專案下面,我Actionbarsherlock是放桌面
至於你的路徑在哪就在哪邊去建立
mkdir libraries
cp -r ~/Desktop/JakeWharton-ActionBarSherlock-071a61c/actionbarsherlock libraries/


在MyApplicationProject目錄下的 settings.gradle 加入這一行
include ':libraries:actionbarsherlock'

在到MyApplicationProject/MyApplication/build.gradle 裡面的 dependencies {...}加入下面
*在這裡我們把compile files('libs/android-support-v4.jar')
因為Actionbarsherlock本身就有引入這個jar就可以拿掉
dependencies {
    compile project(':libraries:actionbarsherlock')
}



接著再 MyApplicationProject/libraries/actionbarsherlock/ 新增 build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.4'
    }
}
apply plugin: 'android-library'
 
dependencies {
    compile files('libs/android-support-v4.jar')
    compile files('libs/android-support-v13.jar')
}
 
android {
    compileSdkVersion 17
    buildToolsVersion "17"
 
    defaultConfig {
        minSdkVersion 7
        targetSdkVersion 16
    }
 
 
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
 
        instrumentTest.setRoot('tests')
    }
 
} 


接著再把它包進去到,在到command line(命令提示字元\終端機)
MyApplicationProject目錄下key
./gradlew build

如果你是Windows,就是下面
./gradlew.bat build

然後他就會開始跑跑跑,跑完就完成!


參考資料:http://rsproglog.blogspot.tw/2013/05/gradle-new-android-build-system_21.html

2013年5月20日 星期一

Android Studio import ActionBarSherlock(1)

Android Studio 這個新的IDE剛起步,有些還不熟悉

就馬上遇到第一個問題!! 怎麼import ActionBarSherlock!!

Google跟亂試,結果得到一個結果


1.首先先去Actionbarsherlock下載或是github



2.Android Studio開新專案,這步驟就當做大家都會!!

3.Android Studio Setting...


  3-1. 開啟 Project Structure

  3-2. 左邊 Project Settings -> Libraries 刪除原先android-support-v4(在ActionBarSherlock已經有import了,所以在這邊刪除他

  3-3. 左邊 Project Settings -> Modules 並在中間點選Import Module加入actionBarSherlock Module



  3-4. 接下來會出現一堆視窗都直接下一步下一步下一步下一步下一步下一步....一直到完成

  3-5. 接著在左邊勾選android-support-v4

  3-6. 接下來點選你的app這裡範例是test就test-test,並在右邊下面點選Module Dependency加入,選取actionBarSherlock



  3-7. 在右邊就會出現actionBarSherlock,接著就勾選起來

  3-8. 大功告成!!



4.測試

測試前

測試後




Android Studio

這次Google I/O 2013發佈一個新的Android IDE - Android Studio!

主要是基於IntelliJ IDEA 做改寫,其中裡面最令人驚訝就是設計Layout的部份

即見即所得以及包含在任何視窗的預覽!

包含版本控制(git), 強大編輯功能

目前現在最大最大的缺點就是....介面及快捷鍵要重新學習  |||orz..

Android Studio - Download


在Google I/O 2013 也有介紹Android Studio


2013年5月7日 星期二

Mac Android use Homebrew

原本Android SDK都從Google官網下載

想說Homebrew不知道能不能下載

沒想到竟然可以,真的很方便

brew install android

這樣就完成了,直接下
android


就會開啟Android SDK Manager下載需要的SDK

路徑位置在
/usr/local/Cellar/android-sdk/r21.1/


參考資料
http://blog.beyondthecorner.co.uk/2011/01/15/android-on-os-x-without-eclipse/

2013年4月1日 星期一

在其他的Service讀取PreferenceActivity所設定的值

經過上次 http://jiun-blog.blogspot.tw/2013/03/preference.html

發現到如果要在其他Service同步偵測,發現在PreferenceActivity所設計的值會有錯誤或抓不到

搜尋後找到了這個方法

public class BackgroundService extends Service {
 
 SharedPreferences.OnSharedPreferenceChangeListener listener;
 
 @Override
 public IBinder onBind(Intent arg0) {
  // TODO Auto-generated method stub
  return null;
 }
 
 @Override
 public void onStart(Intent intent, int startId) {
  EnableSharedPreferences();
 }
 
 @Override
 public void onDestroy() {
  // TODO Auto-generated method stub
  super.onDestroy();
 }
 
 private void EnableSharedPreferences() {
  SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
  listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
   public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    
   }
  };
  prefs.registerOnSharedPreferenceChangeListener(listener);
 }
}

這樣當在PreferenceActivity做了改變,這邊就會偵測到

參考資料:
http://stackoverflow.com/questions/2542938/sharedpreferences-onsharedpreferencechangelistener-not-being-called-consistently

2013年3月31日 星期日

GPS 範圍的計算

最近在開發Android的GPS,在使用Google Map API(JS)看過可以在畫面上畫出一個範圍

就像下面這張

但今天我不是要說Google Map API的使用,至於要怎麼使用就請參考
Google Maps JavaScript API 第 3 版

從上面那張圖的座標點都是從資料庫下來的,但是如果是只要在藍色範圍的座標,就可以參考下面SQL Query

$query = "SELECT ((ACOS(SIN($lat * PI() / 180) * SIN(gps_lat * PI() / 180) + COS($lat * PI() / 180) * COS(gps_lat * PI() / 180) * COS(($lon - gps_long) * PI() / 180)) * 180 / PI()) * 60 * 1.1515 * 1.609344) AS distance, 
gps_lat, gps_long
FROM `mytable` HAVING distance<=$dist ORDER BY distance ASC"

找出你資料庫內有小於這個範圍的資料
這邊的 $lat, $lon為你所要設定的中點GPS(經瑋度),$dist為你所要找的範圍(單位:公里)

比較圖


參考資料:
http://zcentric.com/2010/03/11/calculate-distance-in-mysql-with-latitude-and-longitude/
http://www.marketingtechblog.com/calculate-distance/
http://zh.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

2013年3月27日 星期三

OnItemClickListener 失效

最近有使用到 ListView OnItemClickListener 發現到失效的狀況

搜尋了一下可以用下面這個方法解決

在你的LinearLayout加入下面這段參數,就可以解決失效的問題
android:descendantFocusability="blocksDescendants"


    ......


2013年3月20日 星期三

Preference

在Android App都有像是"設定"的選單,讓使用者去客制自己的選項



這個時候就可以使用Preference來設計你的選單

res/xml/setting
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory android:title="@string/wifi_setting" >
        <CheckBoxPreference
            android:key="@string/test1_key"
            android:defaultValue="true"
            android:title="@string/title1"
            android:summary="@string/summary1" />
   
        <CheckBoxPreference
            android:key="@string/test2_key"
            android:dependency="@string/test1_key"
            android:defaultValue="false"
            android:title="@string/title2" 
            android:summary="@string/summary2" />

        <ListPreference
            android:key="@string/test3_key"
            android:dependency="@string/test1_key"
            android:entries="@array/interval"
            android:entryValues="@array/intervalValues"
            android:defaultValue="30"
            android:title="@string/title3" 
            android:summary="@string/summary3" />
    </PreferenceCategory>
</PreferenceScreen>

我們有時候會看到當選擇了某一項開關,下面的選擇都會被關閉不可選,就是用dependency來指定
只要去指定要follow的key就可以

上面的interval,intervalValues的值就設定在arrary.xml也就是下面

res/value/array.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="wifi_update_interval">
        <item name="10">10 秒鐘</item>
        <item name="30">30 秒鐘</item>
        <item name="60">1 分鐘</item>
        <item name="300">5 分鐘</item>
    </string-array>
    <string-array name="wifi_update_intervalValues">
        <item name="10">10</item>
        <item name="30">30</item>
        <item name="60">60</item>
        <item name="300">300</item>
    </string-array>
</resources>

接著建立一個Option Class
public class option extends PreferenceActivity implements OnPreferenceChangeListener {
 
 /*** Debugging ***/
 private static final String TAG = "option";
 private static final boolean D = true;
 
 
 String test1_key, test2_key, test3_key;
 CheckBoxPreference test1, test2;
 ListPreference test3; 
 
 @Override
 @SuppressWarnings("deprecation")
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  addPreferencesFromResource(R.xml.setting);
  
  getSupportActionBar().setDisplayHomeAsUpEnabled(true);
  
  test1_key = getResources().getString(R.string.test1_key);
  test2_key = getResources().getString(R.string.test2_key);
  test3_key = getResources().getString(R.string.test3_key);
  
  test1   = (CheckBoxPreference) findPreference(test1_key);
  test2   = (CheckBoxPreference) findPreference(test2_key);
  test3   = (ListPreference) findPreference(test3_key);
  
  test1.setOnPreferenceChangeListener(this);
  test2.setOnPreferenceChangeListener(this);
  test3.setOnPreferenceChangeListener(this);
 }
 
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
      case android.R.id.home:
          finish();
          return true;
      default:
          return super.onOptionsItemSelected(item);
     }
 }

 @Override
 public boolean onPreferenceChange(Preference preference, Object newValue) {
  // TODO Auto-generated method stub
  if(D) Log.e(TAG, "Change preference.key: " preference.getKey().toString());
  if(D) Log.e(TAG, "Change preference.key: " newValue.toString());
  
  if(preference.getKey().equals(test1_key)) {

  } else if(preference.getKey().equals(test2_key)) {

  } else if(preference.getKey().equals(test3_key)) {
   
  }
  return true;
 }
}

onPreferenceChange這個Listener會去Listener當值變化的時候,你可以將你的值紀錄起來,這樣大致上就完成
但這個還是有一個問題,你的系統已經紀錄你的option的變化,但你的所設定的defValue卻沒有相同
這時你還需要加入下面來讓你的defValue一樣

public class MainActivity extends Activity {
 
 /*** Debugging ***/
 private static final String TAG = "MainActivity";
 private static final boolean D = true;
  
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
 }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
     if(D) Log.e(TAG, "item.getItemId() "   item.getItemId());
     switch(item.getItemId()) {
     case R.id.menu_settings:
      startActivity(new Intent().setClass(this , option.class));
      break;
     }
        return false;
    }
        
    private void init_option() { 
     SharedPreferences pref  = PreferenceManager.getDefaultSharedPreferences(mContext);
        test1 = pref.getBoolean("test1_key", true);
        test2 = pref.getBoolean("test2_key", false);
        test3 = Integer.parseInt(pref.getString("test3_key", "30"));
    }
}

2012年10月8日 星期一

Android Layout background 設定

在layout要讓物件(Button 等等...)沒有預設的框線

可以在

xml
-----------
android:buckground="@null"



style.xml
-----------
<item name="android:background">@null</item>






http://www.mkyong.com/android/android-imagebutton-selector-example/

2012年9月25日 星期二

Android 用post傳送資料


String ServerAddress = "http://192.168.0.1";
String DataA = "1";
String DataB = "2";
String result = "";
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("AA",DataA ));
params.add(new BasicNameValuePair("BB",DataB ));


HttpPost httpRequest = new HttpPost(ServerAddress);
try {
    httpRequest.setEntity(new UrlEncodedFormEntity(getParams(),HTTP.UTF_8));
    HttpResponse mHttpResponse = new DefaultHttpClient().execute(httpRequest);
    
    if(mHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
        result = EntityUtils.toString(mHttpResponse.getEntity());    
    }
    
} catch (ClientProtocolException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}


宣告一個 List<NameValuePair> 這裡面是等一下要放入要傳的值及傳的名稱
List<NameValuePair> params = new ArrayList<NameValuePair>();

接下來設定傳入名稱及資料,在這裡是傳AA的名稱,DataA是我們的資料

params.add(new BasicNameValuePair("AA",DataA ));


有幾個就以此類推


接著下面是回傳的結果

if(mHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
     result = EntityUtils.toString(mHttpResponse.getEntity());    
}



可以參考下面檔案

https://www.dropbox.com/s/uxzhtg3roeqwycm/ServerRequest.7z






2012年6月29日 星期五

Android 重製ListView

在Google 搜尋這類的資料,其實還蠻多的,大致上都說明的很清楚

其中因為資料過多,當超過6~7筆(依螢幕大小,不同)

在滑動會有資料跳動要注意




在這邊,我是因為部份資料要使用刪除線,但滑動後卻都是刪除線

經過學長的指教,才知道資料一開始會全部已經轉成刪除線,當遇到不用改變時

已經變成刪除線TextView時,當然看到全都是刪除線!!!

所以這時要在另加入恢復成原本,才可以。


2012年6月4日 星期一

Android 讀取JSON

使用方法如下

//將資料寫入JSONArray
JSONArray result = new JSONArray(json_data);
//取出陣列內所有物件
for(int i = 0;i < result.length(); i++) {
    //取出JSON物件
    JSONObject stock_data = result.getJSONObject(i);
    //取得物件內資料
    System.out.println("t:"+stock_data.getString("t"));
    System.out.println("l_cur:"+stock_data.getString("l_cur"));
    System.out.println("c:"+stock_data.getString("c"));
    System.out.println("cp:"+stock_data.getString("cp"));
}


參考資料:http://kie0723.blogspot.tw/2010/12/android-json.html






Android TextView line-through 刪除線

在TextView上加入刪除線

方法如下
TextView textview = (TextView) findViewById(R.id.textview1);
Paint paint = textview.getPaint();
paint.setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
paint.setAntiAlias(true);


轉回來
TextView textview = (TextView) findViewById(R.id.textview1);
Paint paint = textview.getPaint();
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);



使用Android將檔案傳到Server use PHP

首先先說明Android

在這邊是使用HttpClient來實現上傳,程式如下
public void UploadFiles(String PathFile) {
    new Thread() {  
        @Override  
        public void run() {  
            super.run();  
            
            List< NameValuePair> params = new ArrayList< NameValuePair>();
            params.add(new BasicNameValuePair("file",PathFile));
            
            HttpClient client = new DefaultHttpClient();
            HttpPost post = new HttpPost("Server Address/update.php");
            
            try{
                //setup multipart entity
                MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);

                for(int i=0;i< params.size();i++){
                    //identify param type by Key
                    if(params.get(i).getName().equals("file")){
                        File f = new File(params.get(i).getValue());
                        FileBody fileBody = new FileBody(f);
                        entity.addPart("image"+i,fileBody);
                    }else{
                        entity.addPart(params.get(i).getName(),new StringBody(params.get(i).getValue()));
                    }
                }
                post.setEntity(entity);

                //create response handler
                ResponseHandler< String> handler = new BasicResponseHandler();
                //execute and get response
                UploadFilesResponse = new String(client.execute(post,handler).getBytes(),HTTP.UTF_8);
                if(D) Log.e(TAG, "--- response ---"+ UploadFilesResponse);
            }catch(Exception e){
                e.printStackTrace();
            }
        }  
    }.start();  
}

粉紅字是你要上傳檔案的路徑
紅色字是你的Server路徑


接著在Server寫一隻PHP檔案

update.php
<?php 
    if(move_uploaded_file($_FILES['image0']['tmp_name'], "./ImageFiles/".$_FILES['image0']['name'])){
        echo "uploaded";
    }else{
        echo "unsuccessfully";
    }
?>

因為在Android在上傳資料寫法,是使用多檔案上傳的方式,也就是For迴圈那邊,

所以在PHP也應該是這樣寫,但我PHP並沒直接跑迴圈,就直接抓第一筆  $_FILES['image0']


如果在PHP有必要接收兩個檔案以上,直接在裡面加入迴圈,就可以了



最後記得在Server的路徑建立資料夾,不然傳不上去








Android開啟相機,讀取圖片

在Android開啟相機有幾種寫法,有使用預設相機拍攝,另外一種是自己去撰寫相機的APP

對於單純透過相機取得照片,使用預設相機拍攝就好

因為大部份預設都自動對焦、調整相機細節等等,很方便

除非你是要自己寫專屬的相機,就要自己去刻那些Code


在這裡提供使用預設相機拍攝



首先在Manifest加入使用相機的權限
<!-- Camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />


接在著程式寫入
//設定檔名
File tmpFile = new File( Environment.getExternalStorageDirectory(), "image.jpg");
Uri outputFileUri = Uri.fromFile(tmpFile);
 
Intent intent =  new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);    //利用intent去開啟android本身的照相介面 
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri); 
startActivityForResult(intent, 0);

其中裡面的

new File( 路徑, 檔名)

Environment.getExternalStorageDirectory():指SD Card 路徑

這個也是之後拍完照後會儲存至指定的路徑


當拍完後會去丟一個回應,接著再用ImageView物件去顯示,顯示方如下
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);
    if(D) Log.e(TAG, "--- onActivityResult  ---");
    if (resultCode == RESULT_OK) {
        String img_address = Environment.getExternalStorageDirectory()+"image.jpg";
        Bitmap bmp = BitmapFactory.decodeFile(img_address); //利用BitmapFactory去取得剛剛拍照的圖像
        ImageView ivTest = (ImageView)findViewById(R.id.imageView1);
        ivTest.setImageBitmap(bmp);
        
    }
}











2012年5月2日 星期三

在Android使用httpclient傳值讀值

在使用httpclient必需先去 這裡下載 java jar

我是使用HttpClient 4.1.3 (GA) 這個版本

首先先說明如何讀取HTML
public static String getHtmlContent(final String url) {
    String result="";
    HttpGet httpRequest = new HttpGet(url);
    HttpClient httpclient = new DefaultHttpClient();

    try {
        HttpResponse httpResponse = httpclient.execute(httpRequest);
        if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            result = EntityUtils.toString(httpResponse.getEntity());
        }
    } catch (ClientProtocolException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return result;
}

傳入URL後就會讀取HTML

如果要比對字串,會多一個字元,我不知道是不是因為我用PHP的echo的關係
在比對時要再後面多加一個 "\n"

在來是POST資料到指定的網址,並回傳字串
public static String postData(String url, List<NameValuePair> params) {
    String result="";
    HttpPost httpRequest = new HttpPost(url);
    try {
        httpRequest.setEntity(new UrlEncodedFormEntity(params,HTTP.UTF_8));
        HttpResponse httpResponse = new DefaultHttpClient().execute(httpRequest);
        
        if(httpResponse.getStatusLine().getStatusCode() == 200){
            result = EntityUtils.toString(httpResponse.getEntity());
        }
    } catch (ClientProtocolException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return result;
}

不過在使用這函式時要配合使用
List<NameValuePair> params= new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("data","1234567"));
params.add(new BasicNameValuePair("data1","000000"));
postData("http://iccl.nkmu.edu.tw/WSN/getData.php", params)

如果有兩個值就要像上面那樣add兩次,以此類推



2012年4月27日 星期五

C2DM Server(for PHP) (3)

更新:2013/04/01

發現有很多人搜尋到這篇,C2DM已經更名為GCM(Google Cloud Messaging for Android)

幫大家到找一個比較詳細的說明及操作,大家參考吧


參考資料:

http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/

=========================================================================================================================================


Server 這一端不管用什麼Server都可以,可以使用Google App Engine(GAE)或是PHP都可以

如果使用GAE可以參考KEN YANG  http://blog.kenyang.net/2012/03/android-c2dm-sever.html


這裡主要以PHP為主,在C2DM我們需要前面提到SENDER_ID的Email和密碼

因為需要透過驗證得到Auth,透過那個Auth將訊息傳送出去,接下來實作。


我Google了一下發現有人已經有提供C2DM-PHP   https://github.com/lytsing/c2dm-php

不過我使用過後發現不曉得是改版過後還是什麼問題,導致現在這個版本在讀取Auth時

會出現get auth token error錯誤,無法傳送訊息


因此我自己改寫了一下在這裡下載


我稍為節省了一個步驟,在一開始就先把基本資料輸入進去

$c2dm = new c2dm($useremail ,$useremail_passwd , $long_registration_id);

在這邊就會先去跟 https://www.google.com/accounts/ClientLogin 抓你的 auth

錯誤也會在這裡顯示

接著

$c2dm->sendMessage(1,"Hello World!!");

就可以把你的訊息傳送出去,就完成了


其中c2dm.php要注意一下 94行
'data.message'    => $message //TODO: Add your data here.

data.message是data.[key],所以你Android MESSAGE_KEY_ONE 裡的字串是要一樣的
也就是說如果要改變數,PHP和Android兩邊的變數名稱要一樣


大功告成





有興趣可以到以下的參考連結:
http://blog.kenyang.net/2011/12/android-c2dm.html                 :)
http://blog.kenyang.net/2011/12/android-c2dm_22.html           :P
http://blog.kenyang.net/2012/03/android-c2dm-sever.html        :D
http://blog.kenyang.net/2012/03/android-c2dm-android.html     XD






C2DM Sign Up (2)

更新:2013/04/01

發現有很多人搜尋到這篇,C2DM已經更名為GCM(Google Cloud Messaging for Android)

幫大家到找一個比較詳細的說明及操作,大家參考吧


參考資料:

http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/

=========================================================================================================================================


在C2DM 需要到官方去註冊你的App


比較重要
  1. 1.Package name of your Android app * 
  2. Role (sender) account email *
第1個是要和你剛剛Android App Package一樣,第二個也是跟Android 的SENDER_ID一樣
這兩項一定要對,否則可能會有問題。








有興趣可以到以下的參考連結:
http://blog.kenyang.net/2011/12/android-c2dm.html                 :)
http://blog.kenyang.net/2011/12/android-c2dm_22.html           :P
http://blog.kenyang.net/2012/03/android-c2dm-sever.html        :D
http://blog.kenyang.net/2012/03/android-c2dm-android.html     XD

C2DM Android建置 (1)

更新:2013/04/01

發現有很多人搜尋到這篇,C2DM已經更名為GCM(Google Cloud Messaging for Android)

幫大家到找一個比較詳細的說明及操作,大家參考吧


參考資料:

http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/

=========================================================================================================================================



C2DM(Cloud to Device Messaging)GCM(Google Cloud Messaging for Android)

是Google還蠻新的技術,可以透過雲端傳訊到你的手機,詳細說明可以到官網看

https://developers.google.com/android/c2dm/


這項技術需要一些元件,一些限制

Android:  要在2.2以上的手機,且手機需要一個Google帳戶使用者
C2DM Server:  需要去官方去做認證(中間的溝通)
Server:  開發者的Server,主要可以從Server發送訊息出去


Manifest設定
首先先將Android的Manifest做設定,先設定好權限

<!-- Only this application can receive the messages and registration result --> 
<permission android:name="com.example.myapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE" />

<!-- This app has permission to register and receive message -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<!-- Send the registration id to the server -->
<uses-permission android:name="android.permission.INTERNET" />

在<application>裡面加入下面

<service android:name=".C2DMReceiver" />

<!-- Only C2DM servers can send messages for the app. If permission is not set - any other app can generate it --> 
<receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver" 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="com.example.myapp" />
  </intent-filter>
  <!-- Receive the registration id -->
  <intent-filter>
      <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
      <category android:name="com.example.myapp" />
  </intent-filter>
</receiver>

*紅色這邊要注意一下裡面的紅字粗體需要和你的Manifest檔的package的名稱一樣
*藍色部份是官方的一個的檔案(待會再程式再說明)
*橘色部份是待會接收訊息


官方資料:https://developers.google.com/android/c2dm/#manifest



接下來開始寫程式的部份
在這裡我們使用Google的Lib也就是剛剛提到的藍字部份可以點我下載

檔案放至成這樣



接下來程式目錄下新增一個C2DMReceiver.java 也是剛提到的橘色部份


在裡面打上下面的Code
package your.package;

import java.io.IOException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.google.android.c2dm.C2DMBaseReceiver;

public class C2DMReceiver extends C2DMBaseReceiver{
    
    private static final String TAG="C2DMReceiver";
    public C2DMReceiver()
    {
        super(C2DMActivity.SENDER_ID);
    }
    public C2DMReceiver(String senderId) {
        super(senderId);
        // TODO Auto-generated constructor stub
    }
    @Override
    protected void onMessage(Context context, Intent intent) {
        // TODO Auto-generated method stub
        Log.v(TAG, "C2DMReceiver message");
            
        }
    }
    @Override
    public void onError(Context context, String errorId) {
        // TODO Auto-generated method stub
        Log.v(TAG, "C2DMReceiver error");
    }
    
    @Override
    public void onRegistered(Context context, String registrationId)
            throws IOException {
        // TODO Auto-generated method stub
        super.onRegistered(context, registrationId);
        Log.v(TAG, "C2DMReceiver Register:"+registrationId);
    }
    @Override
    public void onUnregistered(Context context) {
        // TODO Auto-generated method stub
        super.onUnregistered(context);
        Log.v(TAG, "C2DMReceiver UnRegister");
    }    
}

大概說明一下
onMessage主要是接收到訊息的會進入的function
onRegistered註冊的時候,這裡要注意一下registrationId,因為在這溝通需要這一組registrationId才能將訊息傳送到App所以在這裡用Log標示出來registrationId

在這裡用一個比較的簡單的方式去寫接收到訊息

@Override
protected void onMessage(Context context, Intent intent) {
    // TODO Auto-generated method stub
    Log.v(TAG, "C2DMReceiver message");
    Bundle extras = intent.getExtras();
    if(extras!=null){
        String msg = (String)extras.get(C2DMActivity.MESSAGE_KEY_ONE);
        Log.v(TAG, "The received msg = "+msg);

        NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        Notification notification = new Notification(R.drawable.ic_launcher, msg, System.currentTimeMillis());
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, C2DMActivity.class), 0);
        notification.setLatestEventInfo(this, getString(R.string.app_name), msg, contentIntent);
        notificationManager.notify(0, notification);
        
    }
}

接到訊息後會跳出一個訊息顯示

基本上在Android 的C2DM的建置已經完成了,接下來回到主要的Activity.java去註冊C2DM

public class C2DMActivity extends Activity {
    /** Called when the activity is first created. */
    //Debugging
    private static final String TAG = "C2DM";
    private static final boolean D = true;
    
    public static final String SENDER_ID = "your email";     //使用C2DM服務的用戶帳號
    public static final String MESSAGE_KEY_ONE = "message";           //與Server接收消息的key值
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Log.v(TAG, "Start");
        C2DMessaging.register(this, SENDER_ID);        //register

    }
}

其中的SENDER_ID只是的你的email也就是等一下會介紹的與C2DM官網註冊的Email
這裡建議不用要個人的Email可以去另外申請一個C2DM專用的
MESSAGE_KEY_ONE這邊是所接收的KEY值必需要相同的名子,也是等會Server那邊會介紹到


到這邊Android的部份就完成了


Android Code:C2DMDemo



有興趣可以到以下的參考連結:
http://blog.kenyang.net/2011/12/android-c2dm.html                 :)
http://blog.kenyang.net/2011/12/android-c2dm_22.html           :P
http://blog.kenyang.net/2012/03/android-c2dm-sever.html        :D
http://blog.kenyang.net/2012/03/android-c2dm-android.html     XD