2013年
今年の抱負でもつらつらと…
去年の反省
2012年 - 明日の鍵
http://d.hatena.ne.jp/tomorrowkey/20120126/132758834
ものづくり
結局昨年と同じくらい。
NFCがんばってた。
毎月どれくらいの収支あるのか把握しておきたい
moneylook 使い始めてだいたい増えてるのか減ってるのか分かるようになった。
レシート受け取りは継続中。
運用的にはもともと経費で落とせそうなヤツだけもらうようにしてたが
とりあえずもらっておいて、あとで仕分けるスタイルにした。
Google I/Oいく
行った。
楽しかった。
しかしGoogle I/Oよりも観光の方が楽しかった感が否めない。
イベント自体よりもエンジニアがたくさん集まるということが楽しい。
つまり楽しい。
今年もがんばって行きたい。
名古屋いく
行けてない。
つ部行ってみたいなぁ、、。
モグ屋いく
行けた。
年明けて間もなくと、年越しに行けた。
ビール大ジョッキがはかどってしょうがない。
レバがうまい。
その他
新しい事を学べたかと思い起こすと、そんなに新しいことしていなくて
環境を整える方に没頭してしまっている。
また、アウトプットぜんぜんない。
プライベートであればメモ程度のものがいくつかあるが
公開しないと意味ないなー。
ブログ書く量が減っているぞ。
今年の抱負
英語
英語を学ぶことは継続が大切だと、多方から聞く。
今年の前半は語学留学に行くので、それをがんばるのはもちろんだが
帰ってきた後に「どのように英語と付き合うか」を考えたい。
仕事
そろそろ「俺が自分でやったほうが早いじゃん」っていうのはやめたい。
自分が痛い目を見る。
仕事を人に任せられるようになりたい。
旅行
ヨーロッパ行きたい。
鹿児島帰りたい。
引越し
練馬いいけど、会社まで遠いわーーーー
移動時間に時間取られるのはもう勘弁><
お金
最近お財布がゆるい。
がんばろう。
#
成長しよう
0次発行FeliCa LiteにNDEFを書き込む
まえがき
Android Advent Calendar 2012 (表)の8日目担当の@tomorrowkey です!
裏は @rukiadia さんです。
がんばります!
いきさつ
0次発行状態のFeliCa LiteにNDEFを書き込めるソフトウェアがなかったので、自分で作りました。
WindowsではPaSoRi+NDEFWriterで、1次発行状態にすると同時にNDEFを書き込むことができます。
NDEFを書き込むために1次発行は必須ではないので、0次発行の状態でNDEFを書き込めるようにしました。
某イベントで2,000枚のFeliCa LiteにSmartPosterを書き込む必要がありました。
当初はPaSoRi+NDEFWriterでやろうかと思ったのですが
1枚でも書き込むためにURLを入力したり、ダイアログのOKボタンを押したりと
オペレーションが煩雑だったのでアプリを作り、連続で書き込めるようにしたのです。
その時はただ同じ値をかければよかったので、マジックナンバーの嵐だったのですが
今回、値を動的に変えられるように、あとわりと綺麗なソースになるように書き直しました。
ライブラリは使っておらず、スクラッチしています。
まだまだ手を抜いてるところがたくさんあるので、なんとかしたいです。
てきとーですが、ここからは各所の解説など書きます。
アプリがFeliCa Liteに反応するようにする
use-permission
NFCを使うためにpermissionの設定が必要です。
/AndroidManifest.xml
<uses-permission android:name="android.permission.NFC" />
launchMode
NFCのIntentはNEW_TASKがついた状態で飛んできます。
いちいち新しいActivityが起動されてはうざいので
launchModeにsingleTaskもしくはsingleInstanceを指定して防ぎます。
android:launchMode="singleTask"
enableForegroundDispatch
NFCが反応したら優先して自分のアプリが起動されるようにforegroundDispatchという機能を使います。
アプリがフォアグラウンドに表示されている状態でNFCをフックしたいので、onResumeに処理を書きます。
あとでonPauseに解除するコードを書けばOKです。
/src/jp/tomorrowkey/android/felicalitewriter/WriteActivity.java
@Override protected void onResume() { super.onResume(); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { Toast.makeText(getApplicationContext(), "not found NFC feature", Toast.LENGTH_SHORT) .show(); finish(); return; } if (!mNfcAdapter.isEnabled()) { Toast.makeText(getApplicationContext(), "NFC feature is not available", Toast.LENGTH_SHORT).show(); finish(); return; } PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0); IntentFilter[] intentFilter = new IntentFilter[] { new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED), }; String[][] techList = new String[][] { { android.nfc.tech.NfcF.class.getName() } }; mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilter, techList); }
pendingIntentにはNFCタグを検出した時に投げて欲しいIntentを入れてあげます。
IntentFilterとtechListには検出したいNFCタグの種類を指定します。
IntentFilterのActionには
- ACTION_NDEF_DISCOVERED
- NDEFフォーマットされたNFCタグ
- ACTION_TECH_DISCOVERED
- NDEFフォーマットされていないタグの中で、タグの種類指定ができる
- ACTION_TAG_DISCOVERED
- どんなタグでもいいから検出する
techListにはタグの種類を指定します。
今回はFeliCa Liteだけに対応したいので
- ActionにACTION_TECH_DISCOVERED
- techListにNfcF
を指定します。
disableForegroundDispatch
アプリがバックグラウンドに入ったときには、NFCフックしなくていいので
有効にしたforegroundDispatchを無効にします。
@Override public void onPause() { super.onPause(); mNfcAdapter.disableForegroundDispatch(this); }
FeliCa の仕様について
ここまで作るとActivity#onNewIntent(:intent)にNFCタグの情報が飛んでくるようになります。
ExtraからTagを引っ張ってNDEFを書いたりするわけですが
中には書けないFeliCa Liteタグがあります。
それはNDEFフラグがたっていないものです。*1
通販等で購入したFeliCa Liteタグはたいていたっていない状態です(あたりまえですね)
つまり、NDEFフラグを立てればいいわけですが
Android SDKでは、FeliCa LiteのNFCフラグをたてる機能がついていません。
自分でポチポチたてる必要があります。
そのためにFeliCa Liteの仕様について知る必要があります。
がんばろう。
一般的なコマンドの体型
コマンド長 : 1 Byte
先頭に全体のコマンド長が入ります。
それはコマンド長を格納する1Byteも含みます。
よく忘れます。
コマンド : 1 Byte
コマンドを指定します。
FeliCa Liteの仕様書に載っているコマンドは以下のもののみです。
- Polling
- 0x00
- Read Without Encryption
- 0x06
- Write Without Encryption
- 0x08
FeliCa Standardにはもうすこしあります。
IDm : 8 Byte
コマンドによりますが、そのあとにはだいたいIDmが入ります。
IDmとはFeliCaタグひとつひとつで一意に識別できるIDのことです。
製造番号みたいなもので、同じ通信領域内に複数のFeliCaタグがあった際に
通信相手を選ぶために使われます。
書き込みコマンドについて
FeliCa Liteには以下のような制限があります。
FeliCa Standardと比べれば考えることが少なくて楽です。
以上のことを踏まえて書き込みコマンドを実装すると以下のようになります。
/** * Write Without Encryptionコマンドを発行します<br> * FeliCa Liteなので、1度のコマンド発行で1ブロックだけ書き込めます * * @param idm IDm * @param blockNumber ブロック番号 * @param data 書き込みデータ * @return レスポンス * @throws TagLostException * @throws IOException */ public byte[] writeWithoutEncryption(byte[] idm, int blockNumber, byte[] data) throws TagLostException, IOException { if (idm == null || idm.length == 0) throw new IllegalArgumentException(); ByteBuffer byteBuffer = ByteBuffer.allocate(31); // Write Without Encryption byteBuffer.put((byte)0x08); // IDm byteBuffer.put(idm); // サービス数 // FeliCa Liteなので1に固定 byteBuffer.put((byte)0x01); // サービスコード(リトルエンディアン) // 0x00 0x09 byteBuffer.put((byte)0x09); byteBuffer.put((byte)0x00); // ブロックリスト // 長さ 2Byteなので1b // アクセスモード FeliCa Liteなので000bに固定 // サービスコード順番 FeliCa Liteなので0000bに固定 // ブロック番号 引数から指定 byteBuffer.put((byte)0x80); byteBuffer.put((byte)blockNumber); // 書き込みデータ byteBuffer.put(data); byte[] command = byteBuffer.array(); byte[] response = executeCommand(command); return response; }
コマンド長はコマンド発行直前に付加するためはぶいてあります。
FeliCa Liteのメモリマップについて
これで書き込みコマンド組立は完成しました。
次にどこになにを書けばいいか調べます。
FeliCa Liteのメモリマップは以下のようになっています。
(これもFeliCa Liteの仕様書に書かれています)
S_PADと書かれた部分が、ユーザーブロックと呼ばれる場所で
ここにさまざまなデータを書き込みます。
そのほかの領域はいろいろな役割や機能があるので詳しくは仕様書を参考にしてください。
ユーザブロック以外にこんかい必要になるのが
MCブロック(メモリーコンフィグレーションブロック)です。
MCブロックのSYS_OP(System Option)に0x01を指定することで
FeliCa LiteがNDEF化されます。
通常は0x00です。
FeliCa LiteのNDEFフラグをたてる
さっそく書き込みコマンドを使ってNDEF化します。
// NDEF化します byte[] data = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, }; felicaLiteTag.writeWithoutEncryption(idm, 0x88, data);
MCブロックに書き込みたいので、ブロック番号に0x88を指定し
データに1ブロック分のデータを指定します。
一度の書き込みで1ブロックすべてを指定しなければなりません。
このブロックの中には一度特定の値を書き込むと、二度とその値を変更できない箇所があります。
ですので、気をつけて書き込みましょう。
本来であれば現在のブロックデータを読み込んでSYS_OPの部分だけ変更したほうがいいんだけど
手を抜きました。
NDEFデータを作る
タグのメタデータっぽいところはNDEFに対応できたから
今度はデータ本体を作ります。
AndroidはNFC対応!だなんて謳ってるくらいだから、NDEFの各フォーマットビルダーくらい用意されてるよなーって
思ったんですが、ありません。
仕様書を読んで自分で作るか、どこかからひろってきましょう。
NDEF仕様書はNFC Forumにあります。
NDEFについては'NFC Data Exchange Format (NDEF) Technical Specification' という仕様書
RTDについては'Record Type Definition Technical Specifications' の各仕様書
を参考にしてください。
NFC Forum : Technical Specifications http://www.nfc-forum.org/specs/spec_list/
以前NDEFについて発表したときの資料はここから
避けては通れないバイナリ地獄 - NDEFってなんだろう - http://www.slideshare.net/tomorrowkey/ndef-13784268
避けては通れないバイナリ地獄 - NDEFってなんだろう - http://www.slideshare.net/tomorrowkey/ndef-13784268
ちなみに身近なところでNDEFデータを作るサンプルとしては
ApiDemosにRTD-Textを作るコードが入っています。
NDEFデータができたら書き込みます。
NDEFフラグを変更した時と同じ要領です。
NDEFヘッダを作成する
データができたところで、S_PAD(スクラッチパッド)に書き込みたいところですが
そのまま書いてもNDEFだと認識してくれません。
実はS_PAD0をAttribute Information Blockにしないといけないためです。
そのへんの仕様はNFC Forumに定義されています。
FeliCa系のタグはNFC ForumではType 3となっているので
NFC Forum Type 3 Tag Operation Specification
という仕様書を見てみると書いてあります。
NFC Forum : Technical Specifications http://www.nfc-forum.org/specs/spec_list/
- Ver
バージョンを指定します。
1.0を表す0x10を指定します。
- Nbr
タグ読み取り時に、一度に読めるブロック数を指定します。
FeliCa Liteなので0x04を指定します。
- Nbw
タグ書き込み時に、一度に書き込めるブロック数を指定します。
FeliCa Liteなので0x01を指定します。
- Nmaxb
NDEFデータとして使えるブロック数を指定します。
13ブロックなので0x00 0x0Dを指定します。
- WriteF
データは一つのタグに収まるので、0x00を指定します。
- RW Flag
今後も書き込みできる状態なので、0x01を指定します。
- Ln
NDEFデータの長さを指定します。
実際のデータ長から設定されます。
- Checksum
チェックサムです。
ブロック0のすべてを加算した値を設定します。
private byte[] createNdefHeader(int ndefLength) { ByteBuffer buffer = ByteBuffer.allocate(16); // Ver buffer.put((byte)0x10); // Nbr // Read Without Encrypitonで一度に読めるブロック数を指定します // FeliCa Liteなので、一度に4ブロック読み込める buffer.put((byte)0x04); // Nbw // Write Without Encryptionで一度に書き込めるブロック数を指定します // FeliCa Liteなので、一度に1ブロック書き込める buffer.put((byte)0x01); // Nmaxb // NDEFとして使用できるブロック数 // FeliCa Liteなので、データ領域は13ブロックまで buffer.put((byte)0x00); buffer.put((byte)0x0d); // unused buffer.put((byte)0x00); buffer.put((byte)0x00); buffer.put((byte)0x00); buffer.put((byte)0x00); // WriteF // 一枚で完結しているので、0x00 buffer.put((byte)0x00); // RW Flag // Read Writeなので0x01 buffer.put((byte)0x01); // Ln // NDEFデータの長さを指定します buffer.put((byte)((ndefLength >>> 16) & 0xff)); buffer.put((byte)((ndefLength >>> 8) & 0xff)); buffer.put((byte)(ndefLength & 0xff)); // Checksum // チェックサムを指定します buffer.put(checksum(buffer.array())); return buffer.array(); } /** * チェックサムを作成します<br> * すべてのバイト配列の合計を計算します * * @param byteArray * @return */ private byte[] checksum(byte[] byteArray) { int sum = 0; for (byte b : byteArray) { sum += b & 0xff; } return new byte[] { (byte)((sum >>> 8) & 0xff), (byte)(sum & 0xff) }; }
NDEFデータを書き込む
これで、すべての材料は揃いました。
ユーザーブロックの0から順にデータを書き込みます。
/** * NdefMessageを書き込みます * * @param idm IDm * @param ndefMessage NDEF * @throws SizeOverflowException NdefMessageのサイズが大きすぎる場合に発生します * @throws TagLostException * @throws IOException */ public void writeNdefMessage(byte[] idm, NdefMessage ndefMessage) throws SizeOverflowException, TagLostException, IOException { if (idm == null || idm.length == 0) throw new IllegalArgumentException(); if (ndefMessage == null) throw new IllegalArgumentException(); byte[][] datas = mappingBlock(ndefMessage); for (int blockNumber = 0; blockNumber <= 13; blockNumber++) { // FIXME レスポンスを握りつぶしているので、どうにかする writeWithoutEncryption(idm, blockNumber, datas[blockNumber]); } } /** * NdefMessageからFeliCa Liteの各ブロックにマッピングします * * @param ndefMessage * @return * @throws SizeOverflowException * @throws IOException */ private byte[][] mappingBlock(NdefMessage ndefMessage) throws SizeOverflowException, IOException { byte[] ndefMessageBytes = ndefMessage.toByteArray(); int ndefMessageBytesLength = ndefMessageBytes.length; int blockCount = (int)Math.ceil(ndefMessageBytesLength / 16.0); if (blockCount > 13) throw new SizeOverflowException(ndefMessageBytesLength, 16 * 13); byte[][] datas = new byte[14][16]; datas[0] = createNdefHeader(ndefMessageBytesLength); ByteArrayInputStream inputStream = new ByteArrayInputStream(ndefMessageBytes); try { inputStream = new ByteArrayInputStream(ndefMessageBytes); int readLength; for (int i = 1; i < datas.length; i++) { readLength = inputStream.read(datas[i]); if (readLength == -1) break; } } finally { try { if (inputStream != null) inputStream.close(); } catch (IOException e) { // ignore } } return datas; }
完成
これでNDEFを書きこめるようになりました。
たのしいですね。
ソースコードはここに晒しておきます。
https://github.com/tomorrowkey/FeliCaLiteWriter
最後に
明日はNexus 7がなかなか届かないことで有名な@sekitoba さんと
@sugimotoak さんです。
みんながんばれー
*1:ユーザーエリアがReadOnlyに変更されている場合もあります
フッタにボタンを表示する4.0
4.0以前はこちら
フッタにボタンを表示する - 明日の鍵 http://d.hatena.ne.jp/tomorrowkey/20110809/1312869978
uninstall_confirm.xml
<!-- OK confirm and cancel buttons. --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:divider="?android:attr/dividerHorizontal" android:showDividers="beginning" android:paddingTop="16dip"> <LinearLayout style="?android:attr/buttonBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:measureWithLargestChild="true"> <LinearLayout android:id="@+id/leftSpacer" android:layout_weight="0.25" android:layout_width="0dip" android:layout_height="wrap_content" android:orientation="horizontal" android:visibility="gone" /> <Button android:id="@+id/cancel_button" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_gravity="left" android:layout_weight="1" android:text="@string/cancel" android:maxLines="2" style="?android:attr/buttonBarButtonStyle" /> <Button android:id="@+id/ok_button" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_weight="1" android:text="@string/ok" android:maxLines="2" style="?android:attr/buttonBarButtonStyle" /> <LinearLayout android:id="@+id/rightSpacer" android:layout_width="0dip" android:layout_weight="0.25" android:layout_height="wrap_content" android:orientation="horizontal" android:visibility="gone" /> </LinearLayout> </LinearLayout>
ボタン1つ
軽く探したけど、だいたいActionBarで実現してるみたい
レイアウトで実装したいなら以下でそれっぽくできる
<LinearLayout style="?android:attr/buttonBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:measureWithLargestChild="true" android:orientation="horizontal" > <Button android:id="@+id/done_button" style="?android:attr/buttonBarButtonStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:drawableLeft="@drawable/ok" android:drawableRight="@drawable/space" android:maxLines="2" android:text="@string/done" /> </LinearLayout>
乗換案内を検索するアプリを作ったよ
毎回駅名を入力して、検索をするなんてめんどうです。
いつも使うルートを予め登録しておけば、あとは選択するだけで
今の時間で検索してくれます
検索結果をわざわざ保存なんてめんどうです。
検索したら自動的に保存してくれます
その他スクリーンショット
Javaの正規表現をテストするサイトを作ったよ
使ったライブラリとか
jQuery: The Write Less, Do More, JavaScript Library
http://jquery.com/
jQueryないとjavascriptがめんどい
zClip :: jQuery ZeroClipboard
Adventures in Programming » Blog Archive » Toast Notifications Using jQuery
http://shawntabai.com/wp/2011/09/06/toast-notifications-using-jquery/
AndroidのToastライクな表示をするためのコード
Google Web Fonts
http://www.google.com/webfonts
Gudeaというフォントを使いました。
チェックボックスをつけて、コードをコピーするだけなんでとても簡単です。
更新しました
- 正規表現エラーだった場合にレスポンスが帰ってこない不具合を直しました
- Bootstrap使いました
- Matchesも表示するようにしました
- Groupのハイライトを色分けしました
2012-02-05 20:45 追記
更新しました
- Findがすべて抽出されないバグを修正しました
2012-02-07 08:49 追記
ソースを公開しました
tomorrowkey/RegularExpressionTester - GitHub https://github.com/tomorrowkey/RegularExpressionTester
いい加減ライセンスの表記をちゃんとしないとなー
2012-02-09 08:40 追記
2012年
いまさら今年の抱負でも書く
去年の反省から
- あんまりものづくりしてなかった。
- 遊んでばかりだった。
- どれだけお金があるのか把握してなかった。
- 経費で落とせそうなヤツは領収書もらう癖ができた。
- 部屋の片付けできた。
- 貯金できた。
- バイク買った。
- 実家帰れた。
今年の抱負
- ものづくりがんばる
- Androidアプリ以外もやる
- 毎月どれくらいの収支あるのか把握しておきたい
- フルマラソン完走
- Google I/Oいく
- 名古屋いく
- 福岡いく
- モグ屋いく
#
すでに1月2月で遊んでばかりなんだけど、冬はしょうがない!
春から本気出す
RealViewSwitcherを作りました
Android Advent Calendar
このエントリはAndroid Advent Calendarのエントリの1つです。
クリスマスまでに1日1記事更新されるので、ぜひご覧になってください。
Android Advent Calendar http://androidadvent.blogspot.com/
RealViewSwitcherを作りました
Launcherアプリのように、1ページごとに切り替わるViewGroupを作りました。
すでにcompatibility packageでViewPagerが公開されていますが
これを作った当時は、まだcompatibility packageがありませんでした。
RealViewSwitcherというViewGroupです。
"RealViewSwitcher"で検索すると同じようなクラスがでてきますが、私に命名センスがないので名前だけ拝借しました。
簡単な使い方
コードから動的にViewの追加も可能ですが、xmlにも記述できます。
通常の独自Viewと同じやり方です。
<?xml version="1.0" encoding="utf-8"?> <view xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/jp.tomorrowkey.android.realviewswitchersample" android:id="@+id/viewSwitcher" android:layout_width="match_parent" android:layout_height="match_parent" class="jp.tomorrowkey.android.realviewswitcher.widget.RealViewSwitcher" app:interpolator="overshoot_interpolator" > <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color1" android:gravity="center" android:text="1" android:textSize="64sp" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color2" android:gravity="center" android:text="2" android:textSize="64sp" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color3" android:gravity="center" android:text="3" android:textSize="64sp" /> </view>
ばいーんの挙動を変更したい場合に限り、
xmlns:app="http://schemas.android.com/apk/res/使用するアプリのパッケージ名"
と
app:interpolator="overshoot_interpolator"
が必要です。
interpolatorに設定可能なリストはここに定義されています。
RealViewSwitcher/res/values/attrs.xml at master from tomorrowkey/RealViewSwitcher - GitHub https://github.com/tomorrowkey/RealViewSwitcher/blob/master/RealViewSwitcher/res/values/attrs.xml
注意点
人によってフリックするときの感覚が違うと思うので、閾値の調整が必要です。
ぜひ、コードを読んで調整してみてください。
ソースコード
tomorrowkey/RealViewSwitcher - GitHub
https://github.com/tomorrowkey/RealViewSwitcher