2015年1月23日金曜日

【PHP】【Android】プッシュ通知 Google Cloud Messaging に連携する Webシステム側から送信のテスト

Web上のシステムから送信し、Androidでそれを受信し、ノーティフィケーションで通知したいような場合。
いわゆるアンドロイドのプッシュ通知の機能。
Web上のシステムとAndroidとをつなぐシステムがGoogleから提供されている。

Google Cloud Messaging という。

参考:
http://www.techdoctranslator.com/android/guide/google/gcm/gs
http://www.techdoctranslator.com/android/guide/google/gcm/gcm
http://dev.classmethod.jp/smartphone/android/gcm/

Android側の実装は上記のそれぞれの記事に色々書いてあるのですが、Web側からの送信のサンプルが少ないので、自作しました。

共有します。

下記ふたつのPHPファイルを、下のソースからコピペして作成し、Webサーバーの同じディレクトリに設置して使ってください。私はXAMPP環境でテストしていました。
◆index.php
◆ClassSenderDataToGoogleCloudMessaging.php

保存文字コードはUTF-8(ボム無し)です。

ClassSenderDataToGoogleCloudMessaging.php のほうは、書き換える場所があります。
◆サーバーアプリAPIのキー:Googleのデベロッパーコンソールから取得します。上のURL参照。
◆端末の登録ID:これはAndroidの側の処理が出来てからじゃないと取得できない値なのでAndroid側の実装を先に頑張る必要があります。Google Cloud Messaging のAPIに端末がアクセスした時得られる値です。

「message」と「url」というふたつのキーの情報が送信される想定です。


///////////////////////////////////////////////////
[1]index.php
///////////////////////////////////////////////////

<?php
require_once("ClassSenderDataToGoogleCloudMessaging.php");

//処理結果通知
$notice_message = "";

//モード取得
if(isset($_REQUEST["mode"])){
    $mode = $_REQUEST["mode"];
}else{
    $mode = "default";
}//if

//送信モードなら送信クラスに処理させる
if($mode == "send"){
    $sender = new ClassSenderDataToGoogleCloudMessaging();
    $result = $sender->sendDataToApi($_REQUEST);
    $notice_message = "<p style='color:red;'>".$result."</p>";
}//if

?>


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Google Cloud Messaging for Android 送信テスト</title>
</head>

<body>
<div>
    <h1>Google Cloud Messaging for Android 送信テスト</h1>


    <!--送信先はこのスクリプト APIへの送信は受け取った後の処理で実行される-->
    <form method="POST" action="index.php">
        <fieldset>
            <p>
                <label>Message</label>
                <input type="text" size="20" name="message">
            </p>
            <p>
                <label>URL</label>
                <input type="text" size="50" name="url">
            </p>
        </fieldset>

        <input type="hidden" name="mode" value="send">
        <input type="submit" value="送信">
    </form>
</div>

<!--実行結果-->
<?php echo("$notice_message") ?>
</body>
</html>




///////////////////////////////////////////////////
[2]ClassSenderDataToGoogleCloudMessaging.php
///////////////////////////////////////////////////
<?php
/**
 * Google Cloud Messaging APIにデータを送信するクラス。
 *
 * Android連携のプッシュ通知の挙動テスト用。
 *
 */

class ClassSenderDataToGoogleCloudMessaging{
    //Google Cloud Messaging API のURL
    private $google_api_url = "https://android.googleapis.com/gcm/send";

    //APIのキー(Googleのデベロッパーコンソールで取得できる「Server API Key(サーバー アプリケーションのAPIキー)」)
    private $api_key = "ここにサーバーアプリケーションのAPIキーを記述";

    //AndroidデバイスIDの配列(まずAndroidからWebAPIに登録してもらう動作をさせ、この値を取得する必要がある)
    //「APA91bEglOye6b4CRq0aACO1ENKHm-V3sOCBgSwIvdSn_UzNioF8jlRftjgR1XSYWk0c8DfOGLcbVuBps3v-PAOsfnvkB4A3ItCDNxlPslI_5BosUNuuZtVGRiZf1Nu1yXa5sGzXuTG0OtjDGu8xcIyHqCPf1Uyi3w」みたいな長い文字列になる
    private $registration_ids = array(
         "ここに端末の登録IDを記述"
        ,"ここに端末の登録IDを記述"
    );


    ///////////////////////////////
    /**
     * APIにデータ送信
     *
     * @param array フォームの送信データ($_REQUEST)
     */
    public function sendDataToApi(
        $request
    ){

        ////////////////////////////
        //送信データ受け取り
        $send_data = $request;


        ////////////////////////////
        //ヘッダーの文字列作成
        $header_string =
             "Content-Type:application/json"."\r\n"
            ."Authorization:key=".$this->api_key."\r\n";


        ////////////////////////////
        //APIへの送信コンテント作成

        //送信コンテント JSON形式
        //参考 : http://dev.classmethod.jp/smartphone/android-tips-14-gcm/
        /*
         * [送信データ]
         * data             : 送りたい文字列(JSON形式で複数可能) ※ 4KBまで
         * registration_ids : 端末ごとの登録IDのリスト(JSON形式) ※一度に1~1000のidを登録可能 ※必須
         * collapse_key     : 送信するメッセージのグループ(任意の文字列)
         * time_to_live     : デバイスがオフラインの場合にメッセージを保持しておく期間(秒で指定)
         */
        $content_array = array(
             'data'             => $send_data
            ,'registration_ids' => $this->registration_ids
            ,'collapse_key'     => 'google_cloud_messaging_test'
           
        );
        //【重要】JSON形式に変換
        $content_json = json_encode($content_array);

        ////////////////////////////
        //コンテキストインスタンスにセットするオプションの配列を作成
        //options は、 $arr['wrapper']['option'] = $value のような形式の、連想配列の連想配列である必要がある
        //参照 : http://jp2.php.net/manual/ja/context.http.php

        $options_array = array();
        //送信メソッド
        $options_array["http"]["method"]  = "POST";
        //ヘッダー
        $options_array["http"]["header"]  = $header_string;
        //コンテント
        $options_array["http"]["content"] = $content_json;


        ////////////////////////////
        //コンテキストインスタンスを作成
        $context = stream_context_create();
        //コンテキストにオプション情報(ヘッダー情報、コンテントの内容、送信メソッドなど)をセット
        stream_context_set_option(
            $context,
            $options_array
        );

        try{
            //APIへのクエリ送信、戻ってきたデータの受取
            $return_data = file_get_contents(
                $this->google_api_url, //接続先のURL
                false,                 //パス検索
                $context               //送信する内容(リソースコンテクスト)
            );

        }catch(Exception $e){
            $return_data = "エラー : APIからのデータ取得に失敗しました。<br>\n".$e;

        }//try

        //データを戻す
        return $return_data;
    }//function

}//class


2015年1月22日木曜日

【ヴァージョン管理システム】コミット→プル→マージ→→プッシュ


Gitやサブバージョンといったヴァージョン管理システムを使って多人数でひとつのプロジェクトをいじる際、よく巻き戻りとかを起こしてしまう。


原因は私にあったようだ。

最近学んだことをメモしておく。

システム:サブヴァージョン
使用ソフト:Tortoise HG

【理解が進んだのでメモ】
コミット:ローカルで自分が行った変更の一覧を作成する。
プル    :リモートで他人によって行われた変更のリストを取得する。
          ここで樹形が枝分かれする。
マージ  :コミットとプルとのふたつの変更リストを突き合わせながら、ふたつを結合する。
          結合した結果、ローカルのレポジトリにそれが反映される。
          ここで樹形が統合される。

----------/ここまでローカルレポジトリの世界/-----------
----------/ここからリモートレポジトリの世界/-----------

プッシュ:マージされた変更を、リモートのリポジトリにアップロードする。



【重要】
枝分かれしてもそれはまだローカルの世界。
枝分かれはプルによって生じる。
プルしてきてもまだそれがいきなりローカルに反映されるわけではない。

コミットもプルも、「候補」である。

マージが重要。
マージによってコミットの候補とプルの候補とを結合する。
マージの結果、樹形が統合される。
マージが終わってもまだそれはローカルの話である。
プッシュによってはじめてリモートに影響が出る。

【Android】ノーティフィケーションにアイコンは必須

 Androidで、以前作ったノーティフィケーション通知クラスをコピペで使いまわそうと思ったら、以前は問題なく動いていたものが動かなくてはまってしまったので、メモ。


結論:アイコンは必須 。セットしないと動かないしエラーも出ない。
備考:ノーティフィケーションビルダーにノーティフィケーションインスタンスを作成させる。

------------------------------------------------------
パッケージ名

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;


/**
 * ノーティフィケーション通知を行うクラス
 */
public class ClassNotificationSender{
    //Singletonデザインパターン : このクラスの唯一のインスタンス
    private static ClassNotificationSender notification_sender = new ClassNotificationSender();

    //ノーティフィケーションのID
    private int notification_id = 0;

    //クラス名
    private static String class_name = "ClassNotificationSender";


    ///////////////////////////////////////////////////////////
    //コンストラクタ
    ///////////////////////////////////////////////////////////
    /**
     * 同じインスタンスはひとつしか作れないようにする
     * Singletonデザインパターン
     */
    private ClassNotificationSender(){
        System.out.println("ClassNotificationSender インスタンスを生成しました。(Singletonデザインパターン)");
    }//function


    ///////////////////////////////////////////////////////////
    /**
     * インスタンス取得
     */
    public static ClassNotificationSender getInstance(){
        return notification_sender;
    }//function


    ///////////////////////////////////////////////////////////
    //メソッド
    ///////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////
    /**
     * プッシュ通知ノーティフィケーション表示。
     *
     * メインアプリ非稼働時にも動作する。
     * インスタンス作成時にコンテキストが得られないようなので
     * このメソッド引数にコンテキストを必要とすることにした。
     *
     * @param  String  通知すべきテキスト
     * @param  Context コンテキストインスタンス
     * @return void
     */
    public void sendNotification(
        String  content_text,
        Context context
    ){
        //nullチェック
        if(
            (content_text == null)||
            (context == null)
        ){
            return;
        }//if

        ////////////////////////////////////
        //ノーティフィケーションインスタンス作成
        //ノーティフィケーションインスタンスを作る際は Notification.Builder を使うのが推奨 APIレベル11以降

        //ノーティフィケーションビルダーインスタンスを作成
        Notification.Builder notification_builder = new Notification.Builder(context);

        //ノーティフィケーションの各種内容をセット
        notification_builder.setTicker("通知あり");                //メインの画面の上部の狭い領域に表示される文字列
        notification_builder.setContentTitle("通知タイトル");      //通知一覧に表示されるタイトル
        notification_builder.setContentText(content_text);         //通知一覧に表示される本文
        notification_builder.setNumber(this.notification_id);      //通し番号
        notification_builder.setSmallIcon(R.drawable.ic_launcher); //注意! アイコンは必須で、無いと表示されないしエラーも出ない

        //ビルダーにノーティフィケーションインスタンスを作成させる
        Notification notification = notification_builder.build();

        //通知表示実行
        //ノーティフィケーションマネジャーインスタンス
        NotificationManager notification_manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        notification_manager.notify(
            context.getPackageName(),    //ノーティフィケーション識別タグ
            this.notification_id,        //ノーティフィケーションに割り振ったID
            notification                 //ノーティフィケーションインスタンス
        );

        System.out.println(class_name + "[通過]ノーティフィケーション発行 : " + notification.toString());

        //ノーティフィケーションのIDをカウントアップ
        this.notification_id++;

    }//function

}//class


2015年1月21日水曜日

【cakePHP】データベース構造を変更したらキャッシュを殺せ

cakePHPを使ってのシステム開発。
データベーステーブルを追加する必要があったのでPHPMyAdminからテーブルを追加。
そのテーブルを受け持つモデルクラスを作成し、find()で動作テスト。



動かん!!!


3時間苦しんだ挙句、原因がわかりました。
キャッシュです。

参考
http://oneday.ter.jp/php/cakephp-php/983.html

\app\tmp\cache\models
に、データベーステーブルとモデルクラスとをつなぐファイルのキャッシュがあるようです。

テーブル構造を変更したら(カラムやテーブルを追加したり名前を変えたりしたら)キャッシュをバシバシ皆殺しにしよう!!!