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);
    }