KLab若手エンジニアの これなぁに?

KLab株式会社の若手エンジニアによる技術ブログです。phpやRubyなどの汎用LLやJavaScript/actionScript等のクライアントサイドの内容、MySQL等のデータベース、その他フレームワークまで幅広く面白い情報を発信します。

Android

NFCとAndroidで端末貸出管理を作ってみた。

福岡事業所のアルバイトmiyachi-nです。 この度Androidに搭載されているNFC機能に興味がわいてきたので、 早速実験がてら弊社のデバッグ用携帯端末の備品管理システムを1人で作ってみました。 NFCタグプリンターは@touchRLさんからお借りしたBrotherのRL-700Sを使っています。 今までは端末の貸出管理を紙に書いて行っていましたが もっと便利なシステムをNFCタグFeliCaとAndroidで作ってみたいとの上司発案ですw


貸出管理システムは以下のような構成になっています。



端末を借りる人の識別は、セキュリティカードに付属するFeliCaで行います。 貸し出す端末の識別は、RL-700SでプリントしたISO15693タグで行います。 この2種類のNFCタグをAndroidアプリで読み取り、貸出情報としてサーバに登録します。 ・ISO15693:RL-700Sで印刷されるNFCタグの一種。 ・FeliCa(セキュリティカード):社員に持たせている入退室用のセキュリティカードNFCタグの一種 ・FeliCa(端末):おサイフケータイのついている端末に付いているFeliCaタグ(チップ)NFCタグの一種 また、現在の貸出状況はブラウザから確認することができます(nodejs上にWebアプリを構築しています)。 これで社員がどのくらいの期間借りているかや所在が不明な端末把握することが可能になります。 また、社員にとっても紙に書くより手間が省け効率が良くなると思います。 こちらの写真がRL-700Sの本体です。意外と小さいです。


こちらの写真がRL-700Sで印刷したタグです白黒ですが 画像やバーコードも付属のアプリケーションで印刷することができます。


使い方:


貸出:Androidアプリで社員証(FeliCa)をスキャン → NFCタグ or 携帯端末のFeliCaをスキャンし、 必要に応じてコメントなどを音声入力してサーバーへ送信して完了です。 返却:返却も簡単で登録時に使ったNFCタグ or 携帯端末のFeliCaをスキャンすれば 返却が完了です。 貸出状況の確認:Webブラウザで貸出状況のページをみれば、現在、誰がどの端末を借りているかを確認することができます。 貸出を行う前には、端末と社員の初期登録が必要になりますが、この作業もAndroidアプリでNFCタグをスキャンして行うことができるようにしました。 メリット: 端末の貸出履歴の情報がデータベース(MySQL)に保存されているので ・現在借りているのはだれか? ・どの端末が頻繁に利用されているか? ・どのキャリアが多く利用されているか? ・どの社員が頻繁に利用しているか? ・貸出理由はどんな理由が多いか? などの情報が簡単に収集ができるようになりました。 これで履歴を見れば事業所内で端末を誰が持っているか(本人も忘れていることが...)をみんなで探す時間が節約できました。 TIPSその1: Google提供のライブラリを使ってISO15693のIDを読み取ろうとすると、 16ビット(2文字程度)を反転して逆の順番となる状態となってしまいました。 これを正しく認識させるために、16ビット(2文字)ずつ反転にして逆にするという処理が必要でした。 こんな感じです。”E004010011E6A01D”→”1DA0E611000104E0” TIPSその2: AndroidからISO15693タグへの書き込みも試したかったので、やってみました ISO15693タグへの書き込みを行うには、まず、タグ自体のフォーマットを行う必要があるのですが、なぜかAndroid 4.0(ISC)にバグがあってISO 15693タイプのタグをフォーマットすることができません。。orz しかし、Android 2.3のNexus SであればISO15693タグをフォーマットできるそうなので 僕が持っているNexus Sでフォーマットを行ってみたところ


キタ━━━━━━(゚∀゚)━━━━━━!! ちゃんとフォーマットしてURLを書きこむことができました!!!! Nexus SでフォーマットしてしまえばAndroid 4.0(ISC)でも書き込みができるようです。


おお!NFCタグをかざすとKLabのホームページも無事表示されます! こういった使い方ができるので色々な応用ができると思います。 RL-700SとNFCタグを貸していただいたり、ISCのバグなど いろいろ教えていただいた@touchRLさんありがとうございました!!!

KLab iPhoroid UI 発表!〜AndroidでリッチなUIを簡単に〜

umjammer です、 弊社の Android 案件の成果物として、リッチなUIを簡単に使用することができるUIコンポーネント群、名付けて「KLab iPhoroid UI」をここに発表します。
KLab iPhoroid UI Logo


Android 案件を進めていく上で、お客様から言われる要件の一つとして「iPhone と同じようなUIにしてもらえませんでしょうか?」というのがあります。やはりスマートフォンと言えば iPhone というイメージが世間では強いのでしょうか? Android には Android UI のポリシーが云々というのはありますが、そこはお客様の要望なので... そして Android のデフォルトで用意されている UI のみでは iPhone の様なリッチな UI & UX を再現するのは困難です。 サードパーティーも頑張って様々なリッチな UI を WEB に発表していますが、イメージの多用、メモリ管理や、大量データの適用等さすがに商用プロダクトとしてそのまま使用できるものはほとんど見当たりません。そんな中頑張って要求された仕様を満たしていった結果、できるだけ今までの Android のプログラミング流儀にのっとったままリッチな UI & UX を実現するコンポーネント群を開発することができました。 そして本日、そのまま商用プロダクトとして使用に耐えうる UI コンポーネント群をオープンソースソフトウェアとして公開します! 早速、作成したコンポーネントをリストアップしていきます。 ListView おなじみリストビューです。無限にページングできます。twitter リストのように上に引っ張れば新規アイテムを取得する処理を実装することも可能です。
iPhoroid ListView


特徴は、
  • twitter ライクな上オーバースクロールによる新規行取得
  • 下オーバースクロールによる無限ページング機能
  • イメージのキャッシュ及びメモリ管理機能
  • スクロール時に無駄な読み込みを抑制する機能
  • 画像読み込み時に無駄な描画を抑える機能
FlowView イメージビューアとして使用できます。ListView や GridView の詳細画面としても使用できます。フリックでアイテムを前後できますのでわざわざ ListView に戻らなくてもアイテムの移動が可能です。
iPhoroid FlowView


特徴は、
  • イメージのキャッシュ及びメモリ管理機能
  • ListViewと同等なOnScrollListenerのイベント機構組み込み
  • スクロール時に無駄な読み込みを抑制する機能
CoverFlow これもおなじみ、カバーフローです。 iPhone で作ったカバーフローよりスクロールスピードが早かったりします。
iPhoroid CoverFlow


特徴は、
  • ListViewと同等なOnScrollListenerのイベント機構組み込み
  • スクロール時に無駄な読み込みを抑制する機能
GridView これは Android オリジナルの UI そのままですが、 KLab iPhoroid UI が提供する Adapter を使用し、(後述)を実装することによって、今まで紹介した UI と同様、
  • ListViewと同等なOnScrollListenerのイベント機構組み込み
  • スクロール時に無駄な読み込みを抑制する機能
を実装することができます。
iPhoroid GridView


もう少し詳しく

キャッシュ&メモリ管理

Android 端末の少ないメモリでは ListView 等で無限にページングしていくと、アイテムの中に画像がある場合などすぐにメモリ不足に陥ってアプリケーションが OutOfMemoryError で落ちてしまいます。そこでデータは都度ネットワーク等からダウンロードして取得するという方向になるのですが、その都度ネットワークからダウンロードすると描画に時間がかかり UX としてよくありません。ここを解決するためにキャッシュを使用します。キャッシュも管理しないとどんどんメモリを食いつぶして同じ結果になるので、うまく不要なキャッシュを破棄する機構が必要になります。WEB を見るとキャッシュに SoftReference を利用しているものを見かけます。SoftReference は試したところ、確かにうまく機能するのですが期待していたよりかなり生存時間が短くなります。あまりキャッシュとしてはよろしくないので KLab iPhoroid UI は LRU アルゴリズムを採用したキャッシュを使用しています。生存時間を指定してキャッシュ機能を調整することもできます。 もう一つキャッシュの破棄に関して厄介なことがあります。Activity が画面遷移で変わってしまった場合裏に行った Activity 上のイメージ等のキャッシュは破棄するべきなのですが、バックボタンを押された時など突然表に戻ってきてしまう場合があります。その時キャッシュクリアにより Bitmap を recycle してしまっていると、使用中の画像がすでに recycle されてしまったと Error が起こりアプリケーションが落ちる場合があります。なので使用中かそうでないかを確認してキャッシュを破棄する機構を実装する必要があります。

無駄な処理の削減と見た目の改善

ListView は用意されている Adapter をそのまま使用すると、表示される以外のアイテムにも描画処理が走り、ネットワークからデータをロードする場合など無駄なロード処理が何度も走ります。これを抑制するためにデータをロードする AsyncTask を管理し、不必要なロードが走っているとキャンセルするようにします。またアイテムにイメージが含まれている場合何度も描画されちらつきます。これも ImageView にフラグを立てることによって無駄な描画処理が走らないように抑制します。 また KLab iPhoroid UI の ListView, FlowView, Coverflow, GridView 共通の機能としてフリック操作での高速スクロールがあります。フリックでスクロールを行なっているときにデータのローディングが発生すると結局スクロールアウトして無駄な処理になってしまいます。そこでフリックのスクロール中はデータをロードしない機構を組み込みます。FlowView, CoverFlow には ListView の様に OnScrollListener をオリジナルのままでは組み込むことができないのでフリック操作を検出するロジックを組み込み ListView と同等な OnScrollListener を使用することができるように改造してあります。 使用方法 長々と詳細を書きましたが使用方法は簡単です。

ListView

ListView には RefreshableArrayAdapter を extends したアダプタ(ここでは ListViewItemAdapter)を設定します。RefreshableArrayAdapter にアイテムを渡す処理で一つ注意点、オーバースクロールで新規アイテムが来たかどうかを判別するためにモデルクラス(ここでは Item)には equals() メソッドを正しく実装する必要があります。あとはデータを取得してくる処理でページングする個数ののアイテムを offset から返す処理(ここではListViewItemDao.getInstance().getItems())を書くだけで自動的に下オーバースクロールでページング、上オーバースクロールで新規アイテム取得処理を行ってくれます。

        :
        listView.setAdapter(new ListViewItemAdapter(ListViewActivity.this, items) {
            public List getItemsOnRefresh(int offset) throws IOException {
                return ListViewItemDao.getInstance().getItems(
                    offset == DefaultRefreshListener.PULL_TO_REFRESH ? 0 : offset);
            }
        });

ListView でイメージを扱う場合は通常通り Adapter.getView() 内でイメージを設定しますが、キャッシュの管理、データロードタスクの管理、フリックでのスクロール中にデータロードをしない機能を組み込むためにユーティリティクラスのメソッド(HasImage.Util.setImage())を使用します。

abstract class ListViewItemAdapter extends RefreshableArrayAdapter<Item> {
    :

    public View getView(int position, View convertView, ViewGroup parent) {
        :

        Item item = this.getItem(position);
        viewHolder.imageView.setTag(position);
        HasImage.Util.setImage(getContext(),
                                                       item.getThumbnailUrl(),
                                                       viewHolder.imageView,
        :

この二つのポイントと(後述)を実装するだけで先述の ListView の特徴がすべて実装されます。簡単でしょう?

FlowView

スクロールを検知するリスナ android.widget.AbsListView.OnScrollListener は ListView 専用です。FlowView でも使用したいので、等価な org.klab.iphoroid.widget.adpterview.OnScrollListener を実装した HasImage.AdapterViewOnScrollListener を代わりにで設定します。 # OnScrollListener は ListView 専用ではなく AdapterView に作ってくれれば全て同様に # 扱えたのに... 今から統合してもらえませんかね? > Google 様 Adapter は何を使用してもらっても結構です。イメージをを扱う場合は ListView 同様 getView() メソッド内で HasImage.Util.setImage() メソッドを用いてイメージを設定してください。

Coverflow

スクロールを検知するリスナは FlowView と同じく HasImage.AdapterViewOnScrollListener をで設定します。 Adapter は CoverFlowImageAdapterBase を extends したクラスを使用します。イメージの設定は他と同じく HasImage.Util.setImage() メソッドを用います。

    :
    coverFlow.setAdapter(new CoverFlowImageAdapter(
        CoverFlowActivity.this, items, 300, 450, true));


public class CoverFlowImageAdapter extends CoverFlowImageAdapterBase<Item> {

    public CoverFlowImageAdapter(Context context,
                                                                 List items,
                                                                 int width,
                                                                 int height,
                                                                 boolean isUserEffect) {
        super(context, items, width, height, isUserEffect);
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView view = new ImageView(this.mContext);

        String url = items.get(position).getImageUrl();
        HasImage.Util.setImage(mContext,
                                                       url,
                                                       view,
            :

GridView

android.widget.GridView は AbsListView.OnScrollListener が設定できるので HasImage.ListViewOnScrollListener のインスタンスを設定してやります。あとは参照のこと。

最後に KLab iPhoroid UI 共通のコーディングとして、フリックでのスクロール中にデータロードをしない機能と、キャッシュのメモリ管理の機能を組み込むために以下のようなコードを KLab iPhoroid UI を使用する Activity に組み込みます。まず Activity には HasImage を implement します。そして OnScrollListener をフィールドに設定して、getScrollState() メソッドでそれを返すように実装します。次にその OnScrollListener を対象の KLab iPhoroid UI に設定します。最後にActivity ライフサイクルに画像、メモリ管理メソッド群(HasImage.Util.onXXX())を実装します。

public class ListViewActivity extends Activity implements HasImage {

    private HasImage.ListViewOnScrollListener onScrollListener;

    public int getScrollState() {
        return onScrollListener.getScrollState();
    }

    :

    public void onCreate(Bundle savedInstanceState) {
        :

        this.listView = (ListView) findViewById(R.id.listView);
        this.onScrollListener = new HasImage.ListViewOnScrollListener();
        listView.setOnScrollListener(onScrollListener);

    :

    protected void onResume() {
        super.onResume();

        HasImage.Util.onResume(this);
    }

    protected void onPause() {
        super.onPause();

        HasImage.Util.onPause(this);
    }

    protected void onDestroy() {
        super.onDestroy();

        HasImage.Util.onDestroy(this);
    }

さらなる詳細は配布物に含まれているデモアプリのコードを参照してください。 オープンソースで公開します KLab iPhoroid UI のコンポーネント群とそれを使用したデモプログラムが含まれています。 配布先は、 https://www.klab.jp/iphoroid/download.html になります。 デモの動画はこちら、


最後に まず KLab iPhoroid UI のベースになったいくつかのオープンソースコードの作者に感謝します。配布物の中にリストアップされています。 次にの部分はほぼ固定のコードですので毎回書くのが冗長ですよね。アノテーションとかうまく使って隠蔽できればと計画中です。 もう一つ、試しに


一つ前の記事
で人任せにしてた自動着色プログラムを KLab iPhoroid UI を使用して作ってみました。いろいろ超手抜きなんですが、それでも1時間ほど(それも大半は自動着色ロジックの移植と画像の縮小ロジック作成にかかった時間です)でコミックビューアが出来上がってしましました。 追記 2012-02-23 ダウンロードのページにデモのリポジトリのURLを記述していなかったため追記しました。

『「MangaMeeya(マンガミーヤ)」 “自動着色”機能すげぇwwwwwwwww』の内情に迫る

umjammer です。 昨日 RSS を眺めていると表題『』内の記事が目に入りました。サイト(エロ広告有り注意)を覗いてみるとたしかにすごいです。久々に面白そうな技術だなと思い、内情を調べてみることにしました。 MangaMeeya は Windows のコミックビューアとして代表的なソフトの一つです(現在は公開中止?)。そして MangaMeeya は AviSynth をプラグインとして使用できるみたいです。AviSynth は様々な動画処理をプラグインを用いて行なう、これも有名なソフトです。私は以前お世話になったことがあります。その AviSynth のプラグインの一つに GiCoCu というものがあり、これが実際の自動着色を行なっていることがわかりました。 まとめると
  • GiCoCu ... 自動着色のロジック
  • AviSynth ... GiCoCu のフロントエンド
  • MangaMeeya ... Avisynth のフロントエンド
ということになります。 ですので MangaMeeya が自動着色を直接やっているわけではありませんでした。すごいのは本来動画処理である AviSynth をプラグインに使用できるようにした発想の方でしょう。動画もフレーム単位で見れば画像処理と変わらないので使えて当たり前なんですが、私には想像できませんでした。すごい! さて、どうやって自動着色を行なうか見て行きましょう。GiCoCu はここを見ると「GIMP の色カーブファイル(*.cur)を使って、色調補正を行うための AviSynth プラグインです。オプションで、Photoshopのトーンカーブファイル(*.amp)にも対応しています。」とあります。このサイトを見ると、擬似四色刷りに調整してある色カーブファイルが既に用意されていて、それを GiCoCu で使用して自動着色を行なうようです。 内情はわかりましたが、私はマカーなので MangaMeeya を簡単には使えません。幸い GiCoCu にソースコードが付属してたので Mac でも動くように移植してみました。移植は簡単に終わったのですが blog に掲載可能なスクリーントーンを多用した漫画風のイメージを WEB で探してもなかなかいいものが見つかりません。仕方ないので、うちのキャバ嬢ちゃんたちに出てもらうことにします。 白黒で地味なキャバ嬢ちゃんたちも、
KyabaBW


巻頭カラーだ!
KyabaColor


まとめ キャバ嬢ちゃんたちは元々カラーの絵なので、白黒にしてもトーンが効いてて自動着色の恩恵をモロに受けてますね。受けすぎてちょっと感動が薄いです。やはりこのサイトのような、もともと白黒のまさにコミックというイメージに色がつくほうが感動が大きいです。 プログラムはこちらです。 擬似四色刷りの色カーブファイルはこちら せっかく Java に移植したので Android のコミックビューア作者さんたち、このロジック搭載してみてはいかがでしょうか?GIMP のソースが入ってるため GPL になりますが。 追記 2012-02-22 人任せじゃ何なので自動着色コミックビューア自作しました。こちらのページからどうぞ

AndroidがAppleに恋をした?(AndroidでAirPlay)

umjammer です、 ひさびさの小ネタです。 Apple はいくつか前のバージョンで AirPlay という機能を iOS や MacOSX に搭載しました。iPhone や iTunes で再生できるメディアを Apple TV2 や AirPlay 対応オーディオ機器にネットワークを介して転送、再生できるというものです。Apple TV2 を AV システムにつないでおくと Mac 手元にオーディオコントロールできます。 ただ、大人の事情かなんなのか iTunes -> iPhone とか Apple TV2 -> iPad とかできないんですよね。ベランダで iPhone で音楽聞きながらネットブラウズとかするときに便利だと思うのは私だけでしょうか?さすがに iPhone に 2万曲入らないし。 AirPlay は一般には仕様が全く公開されていないです。魅力的な機能なのに拘束されて手も足も出ないのは面白くありません。さあ、ハッカーたちの出番です。 ビデオの方は Erica さんに即行ハックされてましたが、オーディオの方は Erica さんも諦めてたように、プロテクトが強固でした。しかし世の中には強者がいるんですねぇ、オーディ転送のプロトコルである RAOP に使用されている暗号化用の秘密鍵を取り出すのに成功しオープンソースとして公開した人がいました。 公開されたのはかなり前でしたが、ShairportAvahi というオープンソースの Bonjour クローンを使用しているため Mac で走らせるのが面倒だったので、一回動くのを試してから放置してました。しばらくぶりにどうなってるのかとサイトを見ていたら Java に移植してる人がいるじゃありませんか! JAirPort
  • jmdns という Pure Java の Bonjour クローンを使用している
  • iTunes に見つけてもらえない、なんで?
RPlay
  • dns_sd という Apple 製のオリジナル Bonjour 実装を JNI で使用している
  • オーディオの再生ができた!
試していてピンときました。 これは Android に移植だと! まず実際に音の鳴った RPlay を採用し JNI の移植を試みたのですが Windows ではうまく行ったのですが Android でうまく行かなくて、デバッグする暇もないので、RPlay の dns_sd を jmdns で置き換える方法にしました。意外とあっさり動きました。 Pure Java になったので Java が動けばどこでも AirPlay 可能になりました。 # PS3 の BD-J で動かしたかったな〜 さあ Android に移植です。 オーディオ再生のところだけ API が違うので Android の API に変更します。ちょっとハマりました。Android のオーディオ API に signed, endian を指定する項目がありません。仕様書にもなにも書かれていないので地道に試すしかありません。そして何度試しても雑音しか出ないんですよね。なんでだろうと探してるとありましたAudioFormat.ENCODING_PCM_16BIT のときは AudioTrack#write(short[],,) を使用するらしいです。だから short[] なんて API があるのかよ!そりゃ endian 指定する必要ないわけだ。
# だとしたら AudioFormat.ENCODING_PCM_16BIT 指定が要らないんじゃあ? あと Android ではバッテリー持ちのためにマルチキャストがデフォルトでは許可されていないので許可してあげます。

    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

    WifiManager.MulticastLock lock;
            :
        WifiManager wifi = (WifiManager)
            getSystemService(android.content.Context.WIFI_SERVICE);
        lock = wifi.createMulticastLock("ShairPort");
        lock.setReferenceCounted(true);
        lock.acquire();
            :
    protected void onDestroy() {
        super.onDestroy();
        if (lock != null) {
            lock.release();
        }
もうひとつ Android 2.2 だと Mac アドレスが JavaSE の様に取れないのでAndroid の API から取得します。

        WifiManager wifi = (WifiManager)
            getSystemService(android.content.Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifi.getConnectionInfo();
        macAddress = wifiInfo.getMacAddress();
完成しました。おー、ちゃんと音鳴るじゃないですか!
[caption id="" align="alignnone" width="407" caption="適当に名前を入れて"]
適当に名前を入れて
[/caption]


[caption id="" align="alignnone" width="289" caption="iTunesで選択"]
iTunesで選択
[/caption]


ソースはここに公開しておきます。 秘密鍵はさすがに付属する度胸はないので Shairport から拾ってきてくださいね。 まとめ Android で AirPlay が動いちゃいました。まあ冒頭に書いたベランダの話くらいしか Android 端末では使用する場面が無いのは確かですが。 しかし Android は電話端末だけじゃないんですよね〜 先を越されたらイヤなので次に何を作るかは秘密です。できたら公開しますね。

C2DMアプリ「Chrome to Phone」を改造しました。

こんにちは。maruyama-r(@h13i32maru)です。 最近C2DMというものを知ったんですが、みなさんはC2DMってご存知ですか?C2DMはGoogleが提供しているもので、Androidアプリに外部からデータをプッシュ配信できる仕組みです。 Android Cloud to Device Messaging Framework - Google Projects for Android そのC2DMを使ったアプリでGoogleが開発している「Chrome to Phone」というのがあります。このChrome to Phoneを使うと、AndroidへGoogleChromeからURLを送信できます。URLを受け取ったChrome to PhoneはURLに応じてブラウザやGoogleマップ、YoutubePlayerなどを自動で起動します。


Chrome to Phoneはサーバサイド、Androidアプリ、Chrome Extensionのソースコードが公開されています。今回はC2DMの勉強をかねてChrome to Phoneのソースコードを読んでいくつか改造してみました。
  • URLを複数のAndroid端末に送信できるようにした
  • URLを別のGoogleChromeに送信できるようにした
  • C2DMのサーバからAndroid端末の登録情報を削除できるようにした


ダウンロードはhttp://h13i32maru.jp/chrometophone/からできます。AndroidアプリとGoogle Chromeにそれぞれインストールして試すことができます。 ※僕が個人的に勝手に改造しただけなので、自己責任でご利用ください。 詳しい内容はスライドにまとめたので興味のある方はご覧ください(C2DMについてもまとめています)



弊社KLabでは毎月ALMという誰でも参加できる社内発表会があります。内容は20分の通常枠と5分のLT枠があります。先月のALMでは「勝手に改造Chrome to Phone」を発表しました。ちょうどのそのときのALMはエンジニア祭りということでピザやお酒を飲みながらいつもと違った雰囲気で行われました。そのときの様子はKLab広報ブログに載っています。こういう発表の場は発表の練習、社内交流、モチベーションアップなんかにつながって非常に良い感じです!しかも発表することでちゃんと評価もされます。と、弊社のいいところ宣伝でしたw
 KLab若手エンジニアブログのフッター