PythonからGoogleのAPIを利用してAndroidへPUSH通知する


GoogleのAPIを用いてAndroid端末へのPUSHとステータスバーへの通知の表示メモ

## クライアント(Android)

AndroidでPUSH通知をするための準備は[http://dev.classmethod.jp/smartphone/android/gcm/](http://dev.classmethod.jp/smartphone/android/gcm/)に書いてあります。
本当に参考になりました。

ここからは上のリンクからの変更点と追加点についてです。

-
Google Developers Consoleでの手順が多少変わっているので、そこについて説明します。

まず、[https://console.developers.google.com/project](https://console.developers.google.com/project)にアクセスし、既存のprojectか"CREATE PROJECT"を選択します。

projectを作成or選択してprojectのページを開きます。
ページ上部の"Project Number"は控えておきましょう。
そして、左側のサイドメニューの"APIs & auth" > "APIs" から"Google Cloud Messaging for Android"のSTATUSをONにします。
なお、公式のドキュメントは[ここ](http://developer.android.com/google/gcm/index.html)です。

次に、"APIs & auth" > "Credentials"で"Public API access"の"CREATE NEW KEY"を選択し、新しくAPI keyを取得します。
表示されるダイアログでは"Server Key"を選択し、IPアドレスは0.0.0.0を入力します。
ここは必要があれば変更しましょう。
テスト用であれば0.0.0.0で問題ありません。多分。

-
GcmBroadcastReceiver.javaは上のリンクのものをそのまま利用させてもらいます。
次に、GcmIntentService.javaを通知のために書き換えます。

```java:GcmIntentService.java
public class GcmIntentService extends IntentService {
    private static final String TAG = GcmIntentService.class.getSimpleName();
    private Notification notification;
    private NotificationManager nm;

    public GcmIntentService() {
        super("GcmIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.v(TAG, "onHandleIntent");
        Bundle extras = intent.getExtras();
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        String messageType = gcm.getMessageType(intent);

        if (!extras.isEmpty()) {
            if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
                Log.d(TAG, "messageType: " + messageType + ",body:" + extras.toString());
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
                Log.d(TAG,"messageType: " + messageType + ",body:" + extras.toString());
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
                Log.d(TAG,"messageType: " + messageType + ",body:" + extras.toString());
            }
        }

		// ここまでhttp://dev.classmethod.jp/smartphone/android/gcm/のものとほぼ同じ
		// extrasに、サーバーからのdataが入っている
		// ここでは、すべて文字列として格納してある
        Log.v(TAG, extras.toString());
        int userId = Integer.parseInt(extras.getString("user_id"));
        String text = extras.getString("text");

		// 通知を開くとMainActivityが表示されるようにする
        Intent i = new Intent(getApplicationContext(), MainActivity.class);
        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        i.setType("notification");
        i.putExtra("user_id", userId);
        i.putExtra("text", text);

		// 第4引数を0にすると、Intentのextrasだけ更新されず、通知のテキストは違うのにextrasだけ同じなため、
		// Intent先のMainActivityでextrasを取得すると同じ値しかとれなくなる事態に
        PendingIntent pi = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);

        Notification.Builder notificationBuilder =
                new Notification.Builder(getApplicationContext())
                        .setContentIntent(pi)
                        .setSmallIcon(R.drawable.icon_small)
                        .setTicker("通知がきました")  // 通知が来た時にステータスバーに表示されるテキスト
                        .setContentTitle("通知のタイトル")  // 通知バーを開いた時に表示されるタイトル
                        .setContentText("通知のテキスト") // タイトルの下に表示されるテキスト
                        .setWhen(System.currentTimeMillis());  // 通知が表示されるタイミング？

        long[] vibrate_ptn = {0, 100, 300, 1000}; // 振動パターン（適当）
        notification = notificationBuilder.build();
        notification.vibrate = vibrate_ptn;
        notification.defaults |= Notification.DEFAULT_LIGHTS; // デフォルトLED点滅パターンを設定
        notification.flags = Notification.FLAG_ONGOING_EVENT;

        // NotificationManagerのインスタンス取得
        nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        nm.notify(R.string.app_name, notification); // 設定したNotificationを通知する
        GcmBroadcastReceiver.completeWakefulIntent(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        nm.cancel(R.string.app_name);
    }
}
```

続いて通知をタップした時の動作として起動されるMainActivityです。
大体上のリンクのものでいいですが、`onCreate`に以下のコードを追加します。 

```java:MainActivity.java
// notification表示関係
Intent intent = getIntent();
String intentType = intent.getType();
if (intentType != null && intentType.equals("notification")) {
	Log.v(TAG + " extras", intent.getExtras().toString());
	// extrasからGcmIntentService.javaでいれたデータを取り出す
	int user_id = intent.getIntExtra("user_id", 0);
	String text = intent.getStringExtra("text");

	NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

	// ダイアログの表示
	Toast.makeText(this, text, Toast.LENGTH_LONG);
}
```

`registerInBackground()`によってLog catにregistrationIdが表示されるので、これを控えます。
アプリケーションとして実装するなら、このIDをサーバーに渡して保存するようにします。

## サーバー(Python)

pythonからは`requests`を利用してGoogleへリクエストを送ります。


```python:api.py
import requests
import json

def push_notificate(user_id, text):
    '''エラーが出るとrにエラーメッセージが入る
	これを呼び出せばAndroidに通知が行く
    '''
    gcm_url = 'https://android.googleapis.com/gcm/send'

	# 上で控えたregistrationIdとAPI key
    regid = REGID
    key = "key=" + APIKEY

    headers = {'content-type': 'application/json', 'Authorization': key}
	# 渡すデータは適当です。
	# dictのkeyはAndroidのextrasのkeyと合わせましょう
    params = json.dumps(\
            {'registration_ids': [regid], \
            'data': {'id': user_id, 'text': text}})

    r = requests.post(gcm_url, data=params, headers=headers)
    print r.text
    return
```

`requests`でbasic認証をクリアするには引数に`auth=(username, password)`を渡しますが、今回はheadersに入れて渡します。


これで大丈夫だと思います。