connvoiのブログ

前だけ向いていけばいい。

AndroidのNotificationの種類

AndroidのNotificationに画像を表示する

Android4.1からはNotification(通知バー)に3パターンの表示方法があって、 最近のアプリだと大体対応してる感じですね。

  • inBoxStyle
  • bigTextStyle
  • bigPictureStyle inBoxはほぼリスト、bigTextは長いテキスト、bigPictureは画像を表示します。

notifications

サンプルを書く

2013-05-26 02.01.39

3つボタンがあって、それぞれを押すと通知される。見たいな感じにします。

MainActivity

ボタン配置をします。まずは3つボタンを作って、ClickListenerで拾います。

  Button bt1,bt2,bt3;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LinearLayout ll=new LinearLayout(this);
        ll.setOrientation(LinearLayout.VERTICAL);
        setContentView(ll);

        bt1 = new Button(this);
        bt1.setText("inboxstye通知");

        bt2 = new Button(this);
        bt2.setText("bigpicturestye通知");

        bt3 = new Button(this);
        bt3.setText("bigtextstye通知");

        ll.addView(bt1);
        ll.addView(bt2);
        ll.addView(bt3);

        bt1.setOnClickListener(new testClickListener());
        bt2.setOnClickListener(new testClickListener());
        bt3.setOnClickListener(new testClickListener());
    }

    class testClickListener implements OnClickListener
    {
        public void onClick(View v)
        {
            //Notitificationの下準備。
            NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
            PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
            NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext());
            if(v == bt1){

                mBuilder
                .setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle("Big notification")
                .setContentText("test of inboxstyle")
                .setAutoCancel(true)
                .setContentIntent(contentIntent);

                //inbox通知ボタンが叩かれたら。
                NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
                String[] events = new String[6];
                //通知された範囲が拡大されたときのタイトル
                inboxStyle.setBigContentTitle("this is inboxstyle");
                //6行文 testと表示します。
                for (int i=0; i < events.length; i++) {
                    inboxStyle.addLine(i+"test");
                }
                //オブジェクトをセット
                mBuilder.setStyle(inboxStyle);

            }else if(v==bt2){
                mBuilder
                .setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle("Big notification")
                .setContentText("test of bigpicturestyle")
                .setAutoCancel(true)
                .setContentIntent(contentIntent);

                //bigPictureStyleの場合は画像を取得する必要が有るので、
                //AsynkTaskを使ってweb上から画像を取ってくるようにします。
                Loadimage task= new Loadimage();
                task.execute("test");
                Bitmap b;
                NotificationCompat.BigPictureStyle bigpictureStyle = new NotificationCompat.BigPictureStyle();
                try {
                    b = task.get();
                    //bitmapをセット
                    bigpictureStyle.bigPicture(b);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                mBuilder.setStyle(bigpictureStyle);

            }else if(v==bt3){
                mBuilder
                .setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle("Big notification")
                .setContentText("test of bigtextstyle")
                .setAutoCancel(true)
                .setContentIntent(contentIntent);

                NotificationCompat.BigTextStyle bigtextStyle = new NotificationCompat.BigTextStyle();
                //bigtextStyleの設定。bigTextに長い文章を置けます。
                bigtextStyle.setBigContentTitle("this is bigtextstyle");
                bigtextStyle.bigText("3.14159 26535 89793 23846 26433 83279 50288").build();
                // Moves the big view style object into the notification object.
                mBuilder.setStyle(bigtextStyle);
            }
            //Notificationをします。
            notificationManager.notify(1,mBuilder.build());

        }

    }

Loadimage.java

特定のurlから画像を取ってくるtaskです。 Notificationに画像をセットする場合、Bitmapである必要があるので、 データをinputstreamで取得、decodestreamでBitmap化する手順を踏みます。

//ResultにBitmapをしていて、Bitmapを返却するAsyncTaskを作成します。
public class Loadimage extends AsyncTask<String, Void, Bitmap> {

    protected Bitmap doInBackground(String...strings){
        Uri.Builder builder = new Uri.Builder();
        builder.scheme("http");
        builder.encodedAuthority(IMAGE_URL);
        builder.path("/bara.png");

        DefaultHttpClient httpClient= new DefaultHttpClient();
        HttpGet request = new HttpGet(builder.build().toString());
        HttpResponse httpResponse=null;
        try {
            httpResponse = httpClient.execute(request);
            int status = httpResponse.getStatusLine().getStatusCode();

            switch(status){
            case HttpStatus.SC_OK:
                //inputstreamをhttpResponse経由で取得
                InputStream is = httpResponse.getEntity().getContent();
                //decodeStreamに食わせてBitmapオブジェクトを作成。*注意pngのみ!
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                is.close();
                return bitmap;
            case HttpStatus.SC_NOT_FOUND:
                return null;
            }
         } catch (ClientProtocolException e) {
              //たくさん有るので省略
         }
        return null;
    }

    public void onPostExecute(Bitmap res){
        super.onPostExecute(res);
    }
}

結果

2013-05-26 02.02.392013-05-26 02.02.552013-05-26 02.03.07

通知バーの一番上に来ると自動的に展開されます。 そしてそして、展開されてない通知もピンチアウトすればなんと展開されます!すごい!

まとめ

  • Notificationは3つのスタイルがある
  • BitmapFactory.decodeStreamはインターネット経由でのjpegはデコードできない。
  • 互換性を考えるなら、NotificationCompat.BuilderをつかってNotificationオブジェクトを作る。

それにしてもさ、 Notificationがピンチアウトで展開、インで閉じるってすっごい微妙ですな。

storm-starterでstormの動作を知る。

リアルタイム分散処理で有名なstormを触ります。 本家にあるtutorialのstrom-starterをやってみる.

インストールから実行

leiningenを入れて、 あとはstorm-starterのコマンド通りにやるだけ。

$brew install leiningen
$git clone git://github.com/nathanmarz/storm-starter.git
$cd storm-starter
$lein deps
$lein compile
#このとき、実行されるのは、src/jvm/storm/starter/Exclamationtopology.java
$java -cp $(lein classpath) storm.starter.ExclamationTopology

実行結果の一部としてこんな表示がでればOK

INFO  backtype.storm.daemon.worker  - Worker e5a06064-17b6-4892-bff2-0e27b72a3e02 for storm test-1-1368162622 on d52ea4b6-b116-4a0f-9761-bb5ae251287c:4 has finished loading
INFO  backtype.storm.daemon.task  - Emitting: word default [jackson]
INFO  backtype.storm.daemon.executor  - Processing received message source: word:5, stream: default, id: {}, [jackson]
INFO  backtype.storm.daemon.task  - Emitting: exclaim1 default [jackson!!!]
INFO  backtype.storm.daemon.executor  - Processing received message source: exclaim1:2, stream: default, id: {}, [jackson!!!]
INFO  backtype.storm.daemon.task  - Emitting: exclaim2 default [jackson!!!!!!]

Exclamationtopology.javaはsploutから生成されたワードに!マークを追加していく物で 1bolt毎に「!!!」を追加していきます。

TopologyBuilder builder = new TopologyBuilder();

builder.setSpout("word", new TestWordSpout(), 1);
builder.setBolt("exclaim1", new ExclamationBolt(), 1)
          .shuffleGrouping("word");
builder.setBolt("exclaim2", new ExclamationBolt(), 2)
          .shuffleGrouping("exclaim1");

Config conf = new Config();
conf.setDebug(true);

WordCountTopology

storm-starterの中に入っているWordCountTopologyを試します。 動作的にはRandomSentenceSpout.javaで発行される文章をsplitして、 ワードカウントをしてきます。

$ java -cp $(lein classpath) storm.starter.WordCountTopology
...
INFO  backtype.storm.daemon.task  - Emitting: split default ["seven"]
INFO  backtype.storm.daemon.task  - Emitting: count default [four, 1]
INFO  backtype.storm.daemon.executor  - Processing received message source: split:3, stream: default, id: {}, ["score"]
INFO  backtype.storm.daemon.task  - Emitting: count default [score, 1]
INFO  backtype.storm.daemon.task  - Emitting: split default ["years"]
INFO  backtype.storm.daemon.executor  - Processing received message source: split:3, stream: default, id: {}, ["and"]
INFO  backtype.storm.daemon.task  - Emitting: count default [and, 1]
INFO  backtype.storm.daemon.executor  - Processing received message source: split:3, stream: default, id: {}, ["seven"]
INFO  backtype.storm.daemon.task  - Emitting: count default [seven, 1]
...

で、ちょっと変更を加えていきます。 RandomSentenceSpoutで発行される文章を適当な物に変更します。 sleepを1000に変更して、 "1000ms毎にhogehoge fofofo か hogehoge fugafuga のどちらかをランダムに発行する" Spoutにします。

@Override                                                              
public void nextTuple() {                                              
    Utils.sleep(1000);
    String[] sentences = new String[] {                                
        "hogehoge fofofo",
        "hogehoge fugafuga"                                            
    };  
    String sentence = sentences[_rand.nextInt(sentences.length)];      
    _collector.emit(new Values(sentence));
} 

WordCountTopology.javaのSetSpout,setBoltのparallelismを1に設定。 これで"RandomSentenceSpout -> SplitSentence -> WordCount" の流れをもったtopologyができると。

public static void main(String[] args) throws Exception {

   TopologyBuilder builder = new TopologyBuilder();
   builder.setSpout("spout", new RandomSentenceSpout(), 1);
   builder.setBolt("split", new SplitSentence(), 1)
          .shuffleGrouping("spout");
   builder.setBolt("count", new WordCount(), 1)
            .fieldsGrouping("split", new Fields("word"));

   Config conf = new Config();
   conf.setDebug(true);

これを実行して、 最終的にhogehogeの回数がfugafugaとfofofoを足した数になってれば意図した通りになってます。

.task  - Emitting: spout default [hogehoge fugafuga]
.executor  - Processing received message source: spout:4, stream: default, id: {}, [hogehoge fugafuga]
.task  - Emitting: split default ["hogehoge"]
.executor  - Processing received message source: split:3, stream: default, id: {}, ["hogehoge"]
.task  - Emitting: count default [hogehoge, 7]
.task  - Emitting: split default ["fugafuga"]
.executor  - Processing received message source: split:3, stream: default, id: {}, ["fugafuga"]
.task  - Emitting: count default [fugafuga, 6]
.task  - Emitting: spout default [hogehoge fofofo]
.executor  - Processing received message source: spout:4, stream: default, id: {}, [hogehoge fofofo]
.task  - Emitting: split default ["hogehoge"]
.executor  - Processing received message source: split:3, stream: default, id: {}, ["hogehoge"]
.task  - Emitting: count default [hogehoge, 8]
.task  - Emitting: split default ["fofofo"]
.executor  - Processing received message source: split:3, stream: default, id: {}, ["fofofo"]
.task  - Emitting: count default [fofofo, 2]

もうちょっと細かくみていく。

SplitSentenceのコンストラクタで書かれてるこれ。 Boltでpythonを読んでます。

public SplitSentence() {
    super("python", "splitsentence.py");
}

githubのstorm-starterのmultilang/resourcesにあるsplitsentence.pyを参照してます。 中身はスペースでsplitしてる物です。
STDINとOUTでやり取りするらしい。 パフォーマンスに影響しないのかな。 https://github.com/nathanmarz/storm/wiki/Multilang-protocol

次にWordCountBoltの受け取り。 受け取ったタプルのワードをカウントして、カウント数とワードをemit

@Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
    String word = tuple.getString(0);
    Integer count = counts.get(word);
    if(count==null) count = 0;
        count++;
        counts.put(word, count);
        collector.emit(new Values(word, count));
    }

WordCountする手前のところで、fieldsgroupingを使っていて、 word毎に流れるタスクを分けてます。

//WordCountTopology.java
builder.setBolt("count", new WordCount(), 12)
        .fieldsGrouping("split", new Fields("word"));

これによって、wordAとwordBがそれぞれ同じgroup通過するようになります。 starterをlocalmodeで動かしてる場合は特に気にしなくても大丈夫。
https://github.com/nathanmarz/storm/wiki/Concepts

まとめ

基本的にはTopologyに書いたSpout,Bolt,Boltの形で処理が実行される。 書き方によってはSpout,Boltの分岐も可能。

parallelism,grouping,worker,taskと分散系のワードが結構合って、
それぞれの意味をちゃんと整理して理解しないと混乱しそう。

https://github.com/nathanmarz/storm/wiki/Understanding-the-parallelism-of-a-Storm-topology つぎはもう少しドキュメントを読んでまとめよう。

FuelPHPのハンズオン的な。

チーム勉強会でFuelPHPについて、インストール〜ちょっと使えるレベルぐらいまで目標にした、 全員参加型のハンズオンをやって見ました。そのとき作った資料です。

設定のところで結構躓いてしまったり、環境ができてないとか合ったけど、 全員scaffoldできて、taskつかってどうやるのー?的なところまで行けたのはよかったです。 ふぁー。

無料でやるFuelPHPでつくるFacebookアプリ

9/20に行われたFuelPHP勉強会に行ってきました。
もう一ヶ月もたってしまった。。。
FuelPHPとFacebookSDK組み合わせて試しにアプリを作って、発表してみました。

・ATND
FuelPHP 勉強会 東京 vol.2

・アプリDEMO
rdital

・発表資料
発表資料

内容

全体の流れ
・phpfogを使う

・FuelPHPにfacebookSDKの読み込み

・アプリ作成

のような感じです。
phpの環境はphpfogを使いました。
お手軽に使うという面で考えると
pagodabox
phpfog
の2つのPaaSがほぼ2強のような感じな気がします。


肝心のアプリ本体の方は、つくってはみたものの、
何となく自分が使うシーンが思いつかなくなってきてしまったので、
しばらく放置になりそうです。。。

感想など

・ninjaauthという便利ライブラリが有るらしい。これ便利だ。
ninjaauth
・phpfog製のfacebookサンプルがGitHubに公開されてて参考になる。
phpfog-facebook
facebookアプリはまだやり尽くされてない感があるのでお手軽に作ってみると面白いかも。
・facebookSDKは意外に簡単に使えた。
・勉強会の半分がGitの話だった。
・元弊社員と勉強会の会場で遭遇。


ということで勉強会は面白かったです。


はじめてのフレームワークとしてのFuelPHP

Androidアプリを作ったよー

Androidアプリのリハビリをかねて(約1年ぶり!?)、久しぶりに作ってみました&公開してみました。
phonegapとかも考えたんですが、まずネイティブで。ってことで、Eclispe+AndroidSDKで書いてます。

光る棒模倣アプリ

サイリュームっぽいアプリです。
画面に2本指を当てて、振ると光ります。


・説明動画


・play
Ylume


実機確認をHTC EVO 3D, HTC Ariaの2台でしかやってないので、動かない可能性は高いです。。。

雑感

Androidは情報がそこかしこに落ちていてかなりお手軽にできるようになってる印象でした。
あとはtestをどうすればいいのかな。その辺も要調査。
やっぱり、お手頃に動く環境があるのはいいですね!!!



iPadキーボード

Untitled

ロジクールbluetoothキーボードを購入。
会議の議事録とか取る用途としては十分です。
値段もそこそこ(4000円)で、キーボード本体の持ち歩きケースがスタンドになるタイプです。
試しにこれ使って書いてます。
徐々にMBAを持ち歩かないでいいようにしたい。


Hackday!

8月末に、恒例のHackdayでした。

Untitled

!!!


今までのhackday発サービス強いられてる

hack cafeの模様




みんなでアイロンがけきれいなしんぶーさん

今回はオリジナルTシャツはバックプリントを入れられるタイプでした。みんなでアイロンがけして楽しかったです。

感想

今回も賞は取れなかったのですが、とても楽しかったです。
ちょっとした勉強会とかの規模を凌駕するイベントが気軽に参加できるのは非常にいいですね。
あとは、やればやるほど自分の技術力の無さに。。。。という展開ですね。



ちーむmessy
messy with you!!