android2.3のNFCを利用してSuicaの入退場記録を表示

Kazzzさんが公開されている nfc-felica http://code.google.com/p/nfc-felica/ をカスタマイズして入退場記録を表示してみました。


入退場記録のフォーマット

以下のサイトを参考にしました。
http://jennychan.web.fc2.com/format/suica.html#108F

カスタマイズ箇所

・入退場記録クラスをSuica.javaに追加

    public static class Inout {
        final byte[] data;
        Context context;  
        /**
         * コンストラクタ
         * @param data データのバイト列(16バイト)をセット
         * @param context androidコンテキストをセット
         */
        public Inout(byte[] data, Context context) {
            this.data = data;
            this.context = context;
        }
        
        public String getConsoleType(){
        	switch(data[0]){
        	case (byte) 0x00:return "清算出場";
        	case (byte) 0x20:return "出場";
        	case (byte) 0x21:return "駅務機器出場";
        	case (byte) 0x40:return "定期出場";
        	case (byte) 0xA0:return "入場";
        	case (byte) 0xC0:return "定期入場";
        	}
			return Byte.toString(data[0]);
        }
        /**
         * 入場駅を取得します
         * @return String バスの場合、序数0に会社名、1停留所名が戻ります 
         *  鉄道の場合、序数0に会社名、1に路線名、2に駅名が戻ります
         */
        public String[] getEntranceStation() {
//                if ( this.isByBus() ) {
//                    //バス利用の場合
//                    return getBusStop(Util.toInt(new byte[]{this.data[6], this.data[7]})
//                            , Util.toInt(new byte[]{this.data[8], this.data[9]}));
//                } else {
                    //鉄道利用の場合
                    return getStation(0, this.data[2], this.data[3]);
//                }
        }





        /**
         *  地区コード、線区コード、駅順コードから駅名を取得します
         * <pre>http://sourceforge.jp/projects/felicalib/wiki/suicaを参考にしています</pre>
         * @param regionCode 地区コードをセット
         * @param lineCode 線区コードをセット
         * @param statioCode 駅順コードをセット
         * @return 取得できた場合、序数0に会社名、1に路線名、2に駅名が戻ります
         */
        private String[] getStation(int regionCode, int lineCode, int statioCode) {
            
            int areaCode = regionCode & 0xff; 
            DBUtil util = new DBUtil(this.context);
            try {
                SQLiteDatabase db = util.openDataBase();
                Cursor c = db.query(TABLE_STATIONCODE
                        , COLUMNS_STATIONCODE
                        ,   COLUMNS_STATIONCODE[0] + " = '" + areaCode + "' and "
                          + COLUMNS_STATIONCODE[1] + " = '" + (lineCode & 0xff) + "' and "
                          + COLUMNS_STATIONCODE[2] + " = '" + (statioCode & 0xff) + "'"
                        , null, null, null, COLUMN_ID);
                
                return ( c.moveToFirst() ) 
                    ?  new String[]{ c.getString(3), c.getString(4), c.getString(5)}
                    :  new String[]{"???", "???", "???"};
            } catch (Exception e) {
                e.printStackTrace();
                return new String[]{"error", "error", "error"};
            } finally {
                util.close();
            }
        }
        /**
         * 処理日付(出場日付)を取得します
         * @return byte[] 
         */
        public Date getProccessDate() {
            int date = Util.toInt(new byte[]{this.data[6], this.data[7]});
            int yy = date >> 9;
            int mm = (date >> 5) & 0xf;
            int dd = date & 0x1f;
            int hh = Util.toInt(new byte[]{(byte) ((this.data[8]&0xf0) >> 4) }) * 10
                     + Util.toInt(new byte[]{(byte) (this.data[8]&0x0f)});
            int min = Util.toInt(new byte[]{(byte) ((this.data[9]&0xf0) >> 4) }) * 10
            + Util.toInt(new byte[]{(byte) (this.data[9]&0x0f)});
            
            Calendar c = Calendar.getInstance();
            c.set(Calendar.YEAR, 2000 + yy);
            c.set(Calendar.MONTH, mm-1);
            c.set(Calendar.DAY_OF_MONTH, dd);

                c.set(Calendar.HOUR_OF_DAY , hh);
                c.set(Calendar.MINUTE, min);
            return c.getTime();
        }
        @Override
        public String toString() {
            NumberFormat nf = NumberFormat.getCurrencyInstance();
            nf.setMaximumFractionDigits(0);
            SimpleDateFormat dfl = new SimpleDateFormat("yyyy/MM/dd HH:mm");
            
            StringBuilder sb = new StringBuilder();
            sb.append("入出場: " + this.getConsoleType() + "\n");
            sb.append("日時: " + dfl.format(this.getProccessDate()) + "\n");

            String[] entranceInfo = this.getEntranceStation();
            
            sb.append("  利用会社: " + entranceInfo[0]+ "\n");
            sb.append("  路線名: " + entranceInfo[1]+ "線\n");
            sb.append("  駅名: " + entranceInfo[2] + "\n");

            return sb.toString();
        }

    }


・NFCFeliCaReader.javaの入退場記録ボタンを有効化

未解決事項

フォーマットは分かったのですが、以下の事項が未解決です。

・入出場、使用装置種別
  → 0byte目のデータですが、すべての値が判明していない

・地域コードがわからない
  → 路線コード(2byte目)&駅コード(3byte目)は判明しているが、
   レコードの中に地域コードの値がない

・バス利用時は入退場に記録されるのか不明
  → こんどバスに乗ったときに履歴を除いてみます