Showing posts with label Push. Show all posts
Showing posts with label Push. Show all posts

Friday, December 9, 2011

Android C2DM client 예제


준비물 : c2dm account(파이썬으로 c2dm서버 만들기 포스팅 참조)

만들 예제의 패키지 구조

우선 Manifest file의 Code



    

    
        
            
                

                
            
        
        
        
  
   
   
    
    
   
   
   
    
    
   
  
  
    
 
 
 

 
 



중요한 부분:
15~ 28 line <-com.leehack.c2dmtest.push.C2DMReceiver 등록
31~36 line <- C2DM을 사용할 수 있도록 어플에 permission부여

C2DMReceiver.java: 리시버 작성

package com.leehack.c2dmtest.push;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;

public class C2DMReceiver extends BroadcastReceiver {
 static String registration_id = null;
 private Context mContext;
 @Override
 public void onReceive(Context context, Intent intent) {
  if (intent.getAction().equals(
    "com.google.android.c2dm.intent.REGISTRATION")) {
   handleRegistration(context, intent);
  } else if (intent.getAction().equals(
    "com.google.android.c2dm.intent.RECEIVE")) {
   handleMessage(context, intent);
  }
 }
 //서버에 등록이 되면 Registration_id를 C2DM서버에서 보내준다. 받은 이 ID를 별도로 구성한 서버에 보내야 한다.
 private void handleRegistration(Context context, Intent intent) {
  String registration = intent.getStringExtra("registration_id");
  if (intent.getStringExtra("error") != null) {
   // Registration failed, should try again later.
  } else if (intent.getStringExtra("unregistered") != null) {
   registration = null;
  } else if (registration != null) {
   registration_id = registration;
   mContext = context;
   SharedPreferences sp  = mContext.getSharedPreferences("com.leehack.c2dmtest", Activity.MODE_PRIVATE);
   SharedPreferences.Editor ed = sp.edit();
   ed.putString("registration_id", registration_id);
   ed.commit();
   Toast toast = Toast.makeText(context, "Registration Id\n" + registration_id,
     Toast.LENGTH_LONG);
   toast.setGravity(Gravity.TOP | Gravity.CENTER, 0, 150);
   toast.show();
   Log.d("C2DMReceiver", "c2dm registered");
  }
 }
 //메세지가 도착하면 토스트로 도착한 메세지를 보여줌
 private void handleMessage(Context context, Intent intent) {

  String c2dm_msg = intent.getExtras().getString("msg");

  System.out.println("c2dm_msg======>" + c2dm_msg);
  Toast toast = Toast.makeText(context, "c2dmMessage\n" + c2dm_msg,
    Toast.LENGTH_LONG);
  toast.setGravity(Gravity.TOP | Gravity.CENTER, 0, 150);
  toast.show();

 }
}

PushHelper.java: c2dm 서버에 Register/Unregister 메소드 작성

package com.leehack.c2dmtest.push;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;

public class PushHelper {

 final static String c2dmDevId = "c2dm에 등록한 account";
 
 public static void c2dmRegister(Context context){
  SharedPreferences sp  = context.getSharedPreferences("com.leehack.c2dmtest", Activity.MODE_PRIVATE);
  if(sp.getString("registration_id", null) != null)
   return;
  Intent registrationIntent = new Intent(
    "com.google.android.c2dm.intent.REGISTER");
  registrationIntent.putExtra("app",
    PendingIntent.getBroadcast(context, 0, new Intent(), 0));
  registrationIntent.putExtra("sender", c2dmDevId);
  context.startService(registrationIntent);
 }
 public static void c2dmUnregister(Context context){
  Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER");
  unregIntent.putExtra("app", PendingIntent.getBroadcast(context, 0, new Intent(), 0));
  context.startService(unregIntent);
 }
}


C2DM_TESTActivity.java: Register와 Unregister버튼의 동작을 정의

package com.leehack.c2dmtest;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

import com.leehack.c2dmtest.push.PushHelper;

public class C2DM_TESTActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        findViewById(R.id.button1).setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    PushHelper.c2dmRegister(C2DM_TESTActivity.this);
   }
  });
        findViewById(R.id.button2).setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    PushHelper.c2dmUnregister(C2DM_TESTActivity.this);
   }
  });
    }
}

앞으로 해야 할일!
1. 구글의 C2DM과 통신한 Push Server 구성
2. Register를 한 후 C2DMReceiver를 통해 C2DM 서버로 부터 전달받은 Registration_ID를 구성한 Push Server로 전달하는 로직 구성
(파이썬으로 c2dm서버 만들기 포스팅을 참조하면 1번은 해결될 것이고 2번만 추가로 구성하면 된다)

Python으로 c2dm server 만들기

1. http://code.google.com/intl/ko-KR/android/c2dm/ 에서 c2dm 서비스에 sign-up
(gmail account를 새로 만들어서 가입하는 것이 좋다. - 실제로 이 account정보를 클라이언트 및 서버에 모두 넣어야 하고 더구나 서버에는 패스워드정보도 필요하기 때문)

2. 가입하면 E-mail로 가입한 Account로 서비스가 Enable되었다는 내용의 메일이 옮(보통 하루안에 오고 늦어도 몇일 사이에는 오는 듯)

여기까지 준비 완료! 아래는 실제 파이썬 코드 시작!!

import urllib, urllib2

class ClientLoginTokenFactory(): 
    _token = None 
    
    def __init__(self):
        self.url = 'https://www.google.com/accounts/ClientLogin'
        self.accountType = 'GOOGLE'
        self.email = 'c2dm에 가입한 메일주소'
        self.password = 'c2dm에 가입한 메일주소의 패스워드'
        self.source = 'replstory-replstory-0'
        self.service = 'ac2dm'
    
    def getToken(self):    
        if self._token is None:
            
            # Build payload
            values = {'accountType' : self.accountType,
                      'Email' : self.email,
                      'Passwd' : self.password, 
                      'source' : self.source, 
                      'service' : self.service}
            
            # Build request
            data = urllib.urlencode(values)
            request = urllib2.Request(self.url, data)
            
            # Post
            response = urllib2.urlopen(request)
            responseAsString = response.read()
            
            # Format response
            responseAsList = responseAsString.split('\n')
            
            self._token = responseAsList[2].split('=')[1]
            
        return self._token
    
class C2DM():
    
    def __init__(self):
        self.url = 'https://android.apis.google.com/c2dm/send'
        self.clientAuth = None
        self.registrationId = None
        self.collapseKey = None
        self.data = {}
        
    def sendMessage(self):
        if self.registrationId == None or self.collapseKey == None:
            return False
        
        clientAuthFactory = ClientLoginTokenFactory()
        self.clientAuth = clientAuthFactory.getToken()
        
        # Build payload
        values = {'registration_id' : self.registrationId,
                  'collapse_key' : self.collapseKey}     
        
        # Loop over any data we want to send
        for k, v in self.data.iteritems():            
            values['data.' + k] = v
        
        # Build request
        headers = {'Authorization': 'GoogleLogin auth=' + self.clientAuth}        
        data = urllib.urlencode(values)
        request = urllib2.Request(self.url, data, headers)
        
        # Post
        try:
            response = urllib2.urlopen(request)
            responseAsString = response.read()
            
            return responseAsString
        except urllib2.HTTPError, e:
            print 'HTTPError ' + str(e)
            
            
sender = C2DM()
sender.registrationId = 'Android단말기에서 c2dm서버로 부터 받은 고유번호'
sender.collapseKey = 1
sender.data = {'msg':'test'}
response = sender.sendMessage()
print response

9,10,79 라인의 정보만 바꾸고 실행하면 c2dm등록된 단말기로 test라는 푸시 메세지가 날아간다.
http://blog.boxedice.com/2010/10/07/android-push-notifications-tutorial/ 에 있는 예제코드인데 제대로 돌지 않아서 약간의 수정만 했음.

이미 눈치 채셨겠지만 실제 사용시에는 78라인부터만 따로 가져가서 원하는 곳에 사용하면 된다. collapseKey를 1로 고정해두고 사용하면 c2dm메세지가 동일한 단말에 동일한 메세지를 계속 적으로 보낼 경우 중간중간 빼먹는 경우가 생긴다. 구글에서 중복된 메세지 전송을 방지하게 넣어둔 코드임. 중복으로 보내는 메세지라도 단말에 꼭 전송이 되어야 하는 메세지라면 collapseKey를 increase하면서 보내는 것이 상책!