2011年7月30日土曜日

NFCを使ってみる その1


NFCを使用した簡単なアプリを作成しようと思います。
まずはTAGを受け取れるようになるまで。

AndroidManifest.xmlに必要なパラメータを記載

NFCハードウェアを使用するために必要なパーミッションを追加
<uses-permission android:name>"android.permission.NFC" />


AndroidMarketにUpする際、NFC機能搭載端末のみ見えるようにするuses-featureを追加
<uses-feature android:name="android.hardware.nfc" android:required"true" />


ActivityでNFCデータをハンドリングしたい場合、inten-filterを追加
<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<data android:mimeType="mime/type"/><
/intent-filter>


<intent-filter>
    <action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>

<intent-filter>
    <action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/nfc_tech_filter" />

必要な<intent-filter>を記載しましょう。

<meta-data ・・・>はandroid.nfc.action.TECH_DISCOVERED用のタグです。
プロジェクトの「/res/xml」に 受け取りたいNFC規格を記載した「nfc_tech_filter.xml」を追加します。

詳しくはDeveloperサイトを参照



    android.nfc.tech.IsoDep
    android.nfc.tech.NfcA
    android.nfc.tech.NfcB
    android.nfc.tech.NfcF
    android.nfc.tech.NfcV
    android.nfc.tech.Ndef
    android.nfc.tech.NdefFormatable
    android.nfc.tech.MifareClassic
    android.nfc.tech.MifareUltralight




必要な<tech>タグのみ記載しましょう。

ちなみに、ここに記載する<tech>は「\frameworks\base\core\java\android\nfc\tech」に存在するクラス名です。


これだけで、SuiccaやICOCAなどのFelicaカードを端末にかざすと、android.nfc.action.XXXXのIntentを受けてActivityが起動します。

2011年6月16日木曜日

Fragmentについて



(作成中)


Fragmentのサンプル

難しい話は置いといて結果的にどうなるのか、
Android 3.1のエミュレータにインストールされている「ApiDemo」でUIを確認。
f:id:baroqueworksdev:20110618043421p:image:w640


サンプルプログラムはこちら。
http://developer.android.com/intl/ja/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html


ActivityにFragmentを追加しているのは以下のコード。

// Check what fragment is currently shown, replace if needed.
DetailsFragment details = (DetailsFragment)
                getFragmentManager().findFragmentById(R.id.details);
if (details == null || details.getShownIndex() != index) {
    // Make new fragment to show this selection.
    details = DetailsFragment.newInstance(index);

    // Execute a transaction, replacing any existing fragment
    // with this one inside the frame.
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.details, details);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.commit();
}


以下の箇所で、「containerViewをR.id.detailsとして、フラグメントdetailsに置き換えます」と指定。
    ft.replace(R.id.details, details);



ft.commit()後、DetailsFragment.onCreateView()がコールされて、フラグメントのViewをActivityに渡している。


Hierarchy ViewでView階層を確認。
ピンク枠がFragmentのcontainerView(親View)
黄色枠がFragmentが持っているView

f:id:baroqueworksdev:20110618044501p:image:w640


GingerBread以前のバージョンでもLayout.xmlを駆使すれば、
見た目は真似できますね。


Fragmentのライフサイクル

FragmentのライフサイクルはActivityのライフサイクルに結び付けられています。

Resume状態になるまでに呼び出されるライフサイクルメソッド

method内容
onAttach()Activityに関連付けれた際に1度だけコールされる
onCreate()fragmentの初期化生成のためによばれる
onCreateView()fragmentのView階層を生成し戻り値として返す
onActivityCreated()Activity.onCreaateが完了したことをframentに通知
onStart()ActivityのonStart()と同等
onResume()ActivityのonResume()と同等


Destroy状態になるまでに呼び出されるライフサイクルメソッド

method内容
onPause()fragment操作が一時停止状態に陥るとき、ActivityのonPause()と同等
onStop()fragment操作が一時停止状態となりユーザーから見えない状態、ActivityのonStop()と同等
onDestroyView()fragmentのView階層をCleanUp可能な状態
onDestroy()fragmentをCleanUp可能な状態
onDetach()Activityとの関連付けが解除された状態


上記のfragmentのライフサイクルとActivityのライフサイクルとの関係を、図に記す。
f:id:baroqueworksdev:20110617020005p:image

(developerサイトから拝借)

(作成中)

参考


http://developer.android.com/intl/ja/reference/android/app/Fragment.html

http://developer.android.com/intl/ja/guide/topics/fundamentals/fragments.html

2011年5月15日日曜日

MOTOROLA XOOM をAndroid OS 3.1にUpdate


やっとOTAがきました

私のXOOM (3G Verizon版)にSystem Updateの通知がきました。

f:id:baroqueworksdev:20110514153203p:image:w480



Update後の「About tablet」を確認すると、ビルドバージョンはHMJ37でした。



f:id:baroqueworksdev:20110514153202p:image:w480





widgetをLongPressすることでサイズ変更の枠が現れました。

青いひし形のマークをドラッグすることでサイズ変更ができました。

f:id:baroqueworksdev:20110514153606p:image:w480

2011年5月14日土曜日

アプリケーションが android:largeHeap=”true”指定されているかどうか確認する


ApplicationInfoの情報


AndroidManifest.xmlに情報を記述するってことは、その情報はApplicationInfoに格納されることは容易に想像がつくと思います。

デベロッパーサイトのApplicationInfoに以下の記載がありました。

public static final int FLAG_LARGE_HEAP

Since: API Level 11

Value for flags: true when the application has requested a large heap for its processes. Corresponds to android:largeHeap.

Constant Value: 1048576 (0x00100000)


ApplicationInfoのpublicメンバー、「flags」のフラグをチェックすれば判断できるってことですね。



MOTOROLA XOOMのインストールアプリを確認


以下のようにインストールアプリの情報を取得して、FLAG_LARGE_HEAPのフラグが立っているアプリのみログ出力してみました。

PackageManager pm = getPackageManager();
if(pm != null){
    List<PackageInfo> package_list = pm.getInstalledPackages(
                    PackageManager.GET_UNINSTALLED_PACKAGES );
    for (PackageInfo info : package_list){
        if(info.applicationInfo != null){
            if((info.applicationInfo.flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0){
                Log.e("FLAG_LARGE_HEAP", info.packageName);
            }
        }
    }
}


結果は以下のアプリでした。

05-15 04:25:42.530: ERROR/FLAG_LARGE_HEAP(7592): com.android.launcher

05-15 04:25:42.530: ERROR/FLAG_LARGE_HEAP(7592): com.google.android.youtube
なんとなく納得できそうなアプリ達でした。




2011年5月13日金曜日

android:largeHeap=”true”で使用可能なHeapサイズ


アプリケーションで使用するHeapサイズを拡大する

APIレベル11(ハニカム)からアプリケーションで使用するHeapサイズを拡大できるらしい。

デベロッパーサイトに以下のような記載があります。

Return the approximate per-application memory class of the current device when an application is running with a large heap. This is the space available for memory-intensive applications; most applications should not need this amount of memory, and should instead stay with the getMemoryClass() limit. The returned value is in megabytes. This may be the same size as getMemoryClass() on memory constrained devices, or it may be significantly larger on devices with a large amount of available RAM.

The is the size of the application's Dalvik heap if it has specified android:largeHeap="true" in its manifest.


AndroidManifest.xmlに以下の記述をすると使用Heapサイズの拡大可能。
android:largeHeap="true"


ActivityManager.getLargeMemoryClass()でサイズを確認。
ActivityManager am = ((ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE));
int largeMemory = am.getLargeMemoryClass();  


ためしに、Motorola Xoom(MZ600) Android OS 3.0で確認したところ、"256MB"でした。


(追記)
Android OS 3.1にUpdate後確認。
Motorola Xoom(MZ600) Android OS 3.1で確認したところ、"256MB"でした。

2011年5月7日土曜日

カスタムNotification Layoutについて


NotificationにカスタムRemoteViewsをセット

NotificationManagerに登録するオブジェクト・Notificationを次のようにすると、カスタムLayoutを表示することができます。


NotificationManager notificationManager = (NotificationManager)context.getSystemService(context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.icon, context.getString(R.string.app_name), 0);
notification.contentView = new RemoteViews(context.getPackageName(),YOUR_CUSTOM_LAYOUT_ID);


下の画像みたいな感じで表示できます。


f:id:baroqueworksdev:20110508020042p:image


各画像からIntentを発行したい

カスタムLayoutが表示できるとなると、次は「各画像をクリックしたら各アプリを起動」したくなります。
RemoteViewsは以下のように各ViewにPendingIntentをセットすることができます。

notification.contentView.setOnClickPendingIntent( VIEW_ID, pendingIntent);


しかし、結論からいうと、FroyoでもGingerBreadでもIntent起動は不可能でした。
(※ただしHTC端末は可能。Android OS 3.0から正式サポートされています)

以下のソースをみれば理解できると思いますが、Intentの発行処理はnotification.contentIntentのみ対象となっています。

\frameworks\base\services\java\com\android\server

  • StatusBarManagerService.java
  • NotificationManagerService.java


Android OS 3.0から正式サポート

各ViewのPendingIntentによる起動がAndroid OS 3.0から正式サポートされています。
デベロッパーサイト Android 3.0 Platform
Support for custom notification layouts to include buttons with PendingIntents, for more interactive notification widgets. For example, a notification can control music playback without starting an activity.


Motorola Xoomで確認すると次のような表示ができました。また、アイコンをクリックするとアプリ起動も可能でした。


f:id:baroqueworksdev:20110508032317p:image

2011年5月4日水曜日

自アプリ以外のアプリをclearDefaultできません

自アプリに対してのみclearDefault可能

PackageManagerクラスのclearPackagePreferredActivities()で各Actionの「デフォルトでの起動」を解除できます。
ただし、Developerサイトに以下の記載されている通り、自アプリに対してのみ使用可能です。"An application can only clear its own package(s)."


プログラムをみてみる

あまりframworkのソースコードまで追いかけるサイトがありませんが、あえて確認の意味で覗いてみます。
該当ソースコードは
\frameworks\base\services\java\com\android\server
 PackageManagerService.java


    public void clearPackagePreferredActivities(String packageName) {
        synchronized (mPackages) {
            int uid = Binder.getCallingUid();
            PackageParser.Package pkg = mPackages.get(packageName);
            if (pkg == null || pkg.applicationInfo.uid != uid) {
                if (mContext.checkCallingOrSelfPermission(
                        android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                        != PackageManager.PERMISSION_GRANTED) {
                    if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
                            < Build.VERSION_CODES.FROYO) {
                        Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
                                + Binder.getCallingUid());
                        return;
                    }
                    mContext.enforceCallingOrSelfPermission(
                            android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
                }
            }

            if (clearPackagePreferredActivitiesLP(packageName)) {
                scheduleWriteSettingsLocked();            
            }
        }
    }



以下のソースでTargetSdkの確認を行い、Froyo以前の場合は問答無用でエラー扱い。
if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
        < Build.VERSION_CODES.FROYO) {


TargetSdkがFroyo以上でも、以下のソースでパーミッションやPidのチェックが行われ、SecurityExceptionとなります。

    private void enforce(
            String permission, int resultOfCheck,
            boolean selfToo, int uid, String message) {
        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException(
                    (message != null ? (message + ": ") : "") +
                    (selfToo
                     ? "Neither user " + uid + " nor current process has "
                     : "User " + uid + " does not have ") +
                    permission +
                    ".");
        }
    }


    public void enforceCallingOrSelfPermission(
            String permission, String message) {
        enforce(permission,
                checkCallingOrSelfPermission(permission),
                true,
                Binder.getCallingUid(),
                message);
    }

2011年5月3日火曜日

Settingsアプリのアプリケーション詳細画面を呼び出してみる

呼び出し方法

APIレベル9よりSettingsクラスに「ACTION_APPLICATION_DETAILS_SETTINGS」が追加されています。
Developerサイトより

Activity Action: Show screen of details about a particular application.
In some cases, a matching Activity may not exist, so ensure you safeguard against this.
Input: The Intent's data URI specifies the application package name to be shown, with the "package" scheme. That is "package:com.my.app".
Output: Nothing.
Constant Value: "android.settings.APPLICATION_DETAILS_SETTINGS"
IntentにはActionと表示したいアプリケーションのpackageスキームを指定します。
ソースコード上はこんな感じでしょうか。
(※actionの指定が直書きですが気にしないでください)


startActivityForResult(new Intent("android.settings.APPLICATION_DETAILS_SETTINGS",
                             Uri.parse("package:[起動したいアプリのPackageName]") )),0); 


2011年5月2日月曜日

検索キーでアプリ起動したい

やりたいこと

検索キー長押しで特定のActivityを起動したい。
デフォルトは音声検索が起動しますが、自分はあまり必要としないので、別のアプリを起動したい。


実現方法

AndroidManifest.xmlにintent-filterを記載すれば起動可能。
<intent-filter>
    <action android:name="android.intent.action.SEARCH_LONG_PRESS" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

2011年4月30日土曜日

新しいAdmobを導入してみた

ライブラリをworkspaceに格納

・「libs」フォルダを作成し、GoogleAdMobAdsSdkXXX.jarを格納する
・プロパティ設定のLibrariesから上記のjarファイルを追加する

詳しくは以下のサイトを参照
Google AdMob Ads Android Fundamentals


AndroidManifest.xmlの変更

以前は<meta-data>が必要でしたが、最新版では不要。
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"</>

<activity android:name="com.google.ads.AdActivity"
          android:configChanges="keyboard|keyboardHidden|orientation"/>



Layoutファイルで定義する場合


xmlns:ads="http://schemas.android.com/apk/res/[PACKAGE_NAME]"

 <com.google.ads.AdView android:id="@+id/adView"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         ads:adUnitId="[YOUR_PUBLISHER_ID]"
                         ads:adSize="BANNER"/>


xmlns:adsの記載を忘れがちなので注意。
valuesフォルダに以下のような「attrs.xml」を作成


<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="com.google.ads.AdView">
      <attr name="adSize">
          <enum name="BANNER" value="1"/>
          <enum name="IAB_MRECT" value="2"/>
          <enum name="IAB_BANNER" value="3"/>
          <enum name="IAB_LEADERBOARD" value="4"/>
      </attr>
      <attr name="adUnitId" format="string"/>
  </declare-styleable>
</resources>



ActivityのJavaソースに記載

・AdView.loadAd(new AdRequest())をコールしないと表示しません
・AdView.setAdListener()でListenerを登録すると何かと便利

    mAdView = (AdView) findViewById(R.id.adView);
     mAdView.setAdListener(new AdListener() {
 
   @Override
   public void onDismissScreen(Ad arg0) {
    // TODO Auto-generated method stub
   }

   @Override
   public void onFailedToReceiveAd(Ad arg0, ErrorCode arg1) {
    // TODO Auto-generated method stub
   }

   @Override
   public void onLeaveApplication(Ad arg0) {
    // TODO Auto-generated method stub
   }

   @Override
   public void onPresentScreen(Ad arg0) {
    // TODO Auto-generated method stub
   }

   @Override
   public void onReceiveAd(Ad arg0) {
    // TODO Auto-generated method stub
   }
        });
     
     mAdView.loadAd(new AdRequest());

2011年4月25日月曜日

twicca マッシュルーム プラグイン Ver0.0.3をリリースしました

twicca マッシュルーム プラグイン Ver0.0.3をリリースしました。
変更点は
・「使用順表示」を追加。使用回数が多いほど

「使用順表示」の選択

設定手順:
1.twicca起動
2.メニュー表示
3.その他→設定→プラグイン設定→ツイート編集プラグイン→マッシュルームプラグイン
4.「使用順表示」のチェックON/OFF

2011年2月9日水曜日

SimpleDateFormatのコンストラクタ

先日のnfcの記事中ソース、
SimpleDateFormat dfl = new SimpleDateFormat("yyyy/MM/dd HH:mm");

がローカライズされていなんじゃないかとご指摘いただきました。
はい、その通りです。ローカライズされていません。
android.comのreference参照

じゃあ、ローカライズされたフォーマッタが必要な場合どうしたらいいの???
それは、以下のDateFormatクラスのファクトリーメソッドから取得可能です。
DateFormat df = DateFormat.getDateInstance()
DateFormat df = DateFormat.getDateTimeInstance()
DateFormat df = DateFormat.getTimeInstance()
(※SimpleDateFormatのクラスが使いたいなら、SimpleDateFormatにキャスト)
android.comのreference、DateFormat参照

アプリケーションのUI部分でローカライズされた日時を表示するときは、
上記のファクトリーメソッドから取得すればいいですね~。

2011年2月7日月曜日

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

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


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

以下のサイトを参考にしました。
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]);
//                }
        }





        /**
         *  地区コード、線区コード、駅順コードから駅名を取得します
         * 
http://sourceforge.jp/projects/felicalib/wiki/suicaを参考にしています
* @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目)は判明しているが、
   レコードの中に地域コードの値がない
・バス利用時は入退場に記録されるのか不明
  → こんどバスに乗ったときに履歴を除いてみます

2011年1月15日土曜日

twicca マッシュルーム プラグイン Ver0.0.2をリリースしました

twicca マッシュルーム プラグイン Ver0.0.2をリリースしました。
変更点は
・各マッシュルームごとに「変換モード」と「挿入モード」の選択
です。

「変換モード」と「挿入モード」の選択


設定手順:
1.twicca起動
2.メニュー表示
3.その他→設定→プラグイン設定→ツイート編集プラグイン→マッシュルームプラグイン


f:id:baroqueworksdev:20110116032737p:image 

f:id:baroqueworksdev:20110116032736p:image