« 2009年01月 | メイン | 2009年03月 »

2009年02月 アーカイブ

2009年02月05日

「Android SDK WG 第3回 セッション withプチ勉強会」を開催しました

私は「日本Androidの会」というところで、Android SDK WGというワーキンググループのリーダーをしています。
Android SDK WGとは、一言で言うと
「Android SDKの理解を深めながら、開発者同士仲良くやろうぜ の会」
です。皆で休日集まって、午後の時間を丸々使って勉強し、その後ご飯を食べに突入します。

先日、「Android SDK WG 第3回 セッション withプチ勉強会」というイベントを開催しました。
今回は50名くらい集まりました。私は実機を使って皆でセンサーで遊ぶ発表をしたのですが、G1やDevPhoneも15台くらい集まりました。
Android SDK WG 第3回 セッション withプチ勉強会

Android SDK WGでの活動は、今回が3回目です。今までこんなことをやってきました。

Android SDK WG 第1回 セッション(2008.10.25)
Android SDK WG 第2回 セッション(2008.11.29)
Android SDK WG 第3回 セッション withプチ勉強会

定期的に開催していますので、もしご興味があればぜひお気軽に御参加下さい。

2009年02月06日

Android Developers Blog - Can I use this Intent?

Android Developers Blogの、“Can I use this Intent?”というポストが役に立ちそうだった、かつ、誰も翻訳してなさそうだったので日本語にしてみました。訳はそれほど正確ではありませんので、オリジナルの方もご覧下さい。

http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html


Can I use this Intent? - このIntentって使える? 2009/1/5 Romain Guy

 Androidは、Intentと呼ばれるとてもパワフルで使いやすいツールを提供しています。Intentによって、アプリケーションをハイレベルなライブラリとして利用することが出来ますし、従来よりもコードを再利用しやすくなります。例えば、AndroidのホームスクリーンとAnyCutアプリは、ショートカットキーを定義するためにIntentをたくさん使っています。疎結合なAPIを使って作れることはとてもナイスなんですが、その反面、あなたが送ったIntentが別のアプリケーションで受け取られているという保証はありません。これは、「Panoramio」とその「RADAR Intent」のような、サードパーティのアプリで特に起こりがちです。

 私は新しいアプリを作っている最中に、あなたが使いたいIntentに反応してくれるアプリケーションがシステム中に存在するかどうかを知る、とても簡単な方法があることを思いつきました。私のアプリではメニュー項目をユーザーがクリックするとIntentが出るようになってるのですが、この方法を使って(利用できない)メニュー項目を灰色表示にするよう実装しました。コードはとてもシンプルで追いかけるのも簡単です。

/**
 * 引数で指定したActionを使えるIntentがあるかどうかを返します。
 * このメソッドは、引数で指定したActionを設定したIntentに応答できる
 * インストール済パッケージをパッケージマネージャーに問い合わせます。
 * 適切なパッケージが存在しない場合はfalseを返します。
 *
 * @param context アプリのContext
 * @param action 使えるかどうかチェックしたいAction文字列
 *
 * @return true:引数で指定したActionが設定されたIntentを扱える場合
 *         false:それ以外
 */
public static boolean isIntentAvailable(Context context, String action) {
    final PackageManager packageManager = context.getPackageManager();
    final Intent intent = new Intent(action);
    List<ResolveInfo> list =
            packageManager.queryIntentActivities(intent,
                    PackageManager.MATCH_DEFAULT_ONLY);
    return list.size() > 0;
}

使い方はこんな感じです。

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    final boolean scanAvailable = isIntentAvailable(this,
        "com.google.zxing.client.android.SCAN");

    MenuItem item;
    item = menu.findItem(R.id.menu_item_add);
    item.setEnabled(scanAvailable);

    return super.onPrepareOptionsMenu(menu);
}

この例では、バーコードスキャナのアプリがインストールされていなければメニュー項目を灰色表示にします。他のもっとシンプルな方法としては、startActivityメソッドを呼ぶときにActivityNotFoundException例外をキャッチする方法があります。しかしこの方法は、問題が発生した時に対応することしか出来ません。したがって、ユーザーがうまく動作しない何らかの操作をしてしまうことを防止するために、事前に予測してUIを更新することが出来ません。ここで示したテクニックは、足りないパッケージをインストールするかどうかをアプリ起動時にユーザーに尋ね、適切なURIを使ってAndroid Marketへリダイレクトするといったような使い方も出来ます。

注釈:この記事のオリジナルは私の個人BLOGの投稿です。

2009年02月10日

Android SDK 1.1 リリース - APIレベルについて

Android SDK 1.1がリリースされました。Android SDK 1.1では、アプリがサポートしているAPIレベル(APIのバージョンのようなもの)を指定することが出来るようになっているようです。Android 1.1 Version Notesの、APIレベルに関係するOverviewのみ、ノリで日本語にしてみました。訳はそれほど正確ではありませんので、オリジナルの方もご覧下さい。


Android 1.1 Version Notes - Overview

 SDKに含まれるAndroid 1.1のシステムイメージは、Android 1.1のプロダクションシステムイメージと開発用途に互換を持たせたものです。Android 1.1のプロダクションシステムイメージは2009年2月からAndroid搭載端末にデプロイ可能です。
 Android 1.1のシステムイメージは、フレームワークのAPIも更新されて配布されています。Android 1.1のAPIでは、「2」のように数値で識別子をつけてシステムに記録しておくことが出来ます。このことはAndroid 1.0のAPIも同様です。この識別子は「APIレベル」と呼ばれ、システム上でアプリが正しく動作するかどうかを、そのアプリをインストールする前に正確に判断することができるようにするためのものです。
 マニフェストファイルにはAPIレベルの値を定義しておくことができます。これは、そのアプリが動作するにあたって必要となるAndroidのシステムのミニマムバージョンを示します。APIレベルを指定するには、マニフェストファイルにminSdkVersion属性を設定します。この属性には、APIレベルを特定する数値を設定します。システムはアプリをインストールする前にこのminSdkVersionの値をチェックして、システム自体に記録されているAPIレベル以下の値のもののみインストールを認めます。
 もし、Android 1.1のプラットフォームが動作しているAndroid端末と互換性を持たせたアプリを、Android1.1のシステムイメージを使って作成したら、minSdkVersionの値は「2」(Android 1.1と厳密に対応したAPIであることを示します)にする必要があることに注意して下さい。
 具体的には、マニフェストファイルの<manifest>要素の子要素として<uses-sdk>を置き、そこにminSdkVersion属性を定義します。以下のような感じです。

<manifest>
  ...
  <uses-sdk minSdkVersion="2" />
  ...
</manifest>

 このようにminSdkVersionを設定することによって、ユーザーはそのアプリをAndroid 1.1プラットフォームの端末にのみインストールできるようになります。言い換えると、1.1のAPIを使って作成されているそのアプリケーションはこれらの端末でちゃんと動作することを示しています。
 もし、アプリを1.1のAPIを使って作ったのに<uses-sdk minSdkVersion="2" />を書いていなかったら、Android 1.1の端末上ではちゃんと動くでしょうが、1.0の端末では動かないでしょう。後者の場合、実行時に1.1のAPIを使おうとした時点でアプリがクラッシュしてしまうでしょう。
 もしアプリに1.1で新しく公開されたAPIを全く使ってないのなら、minSdkVersionを設定しないようにするか、minSdkVersionに「1」を設定することで、Android 1.0に互換があることを示すことが出来ます。しかし、アプリケーションの公開前にAndroid 1.0のシステムイメージ(Android 1.0 SDKで利用できます)を対象にして、アプリケーションがちゃんとコンパイル出来るかを確認しなければなりません。このことはAndroid 1.0の端末でちゃんと動くことを保証することにつながります。アプリが互換性を持つAPIレベルと対応するシステムイメージを使ってアプリをテストする必要があります。
もし、アプリに1.1のAPIを使っておらず、使う必要がないとはっきり判っているならば、Android 1.0のSDKをそのまま使い続ける方が、1.1を使って追加でテストするよりも楽かもしれません。

2009年02月14日

Android - 加速度センサー再び

私が以前書いた、これ、や、これ、などのセンサーの投稿は、結構内容が古くなってしまいました。

 これを調べていた頃はまだG1が出る前(9月)だったので、その当時のOpenIntentsのSensorSimulatorの振る舞いや、当時のAPIリファレンスを見ながら書いたものです。しかし、実際にDev Phoneを購入して試してみるとDev Phoneで実際に取得できるセンサーの値がちょっと違っていることがわかりました。また加えてAPIに拡張や変更があったことも原因で、前に書いた記事を鵜呑みにすると、うまくいかないことがあります。ググってもAndroid SDKの加速度センサーを使った日本語の情報があまり出てこないようで、私のこのBLOGに辿り着かれる方が結構いらっしゃるので、ずっと修正したいと思っていました。

 全て網羅するわけではないのですが、いくつかの差異をここに示そうと思います。もしかすると、G1特有の挙動かもしれず、別機種ではまた別の値となるかもしれませんが、ご参考までに。


加速度センサーの値:
 以前は、1Gのときに1.0fを示すと書きましたが、仕様が変更になったようで、m/s^2のままです。なので、1Gのときは9.82fになるのだと思います。1Gのときに1.0にしたい場合はSensorManager.GRAVITY_EARTHで割ると出てきます。地球以外で使う場合は別の値で割ればいいと思います。


傾度センサーの値:
 以前は、0番目からyaw, pitch, rollと思っていましたが、違うようです。通常のロールピッチヨー角の考え方とは異なります。0番目はyawでなく、代わりにAzimuthという方位を示す値が入ります。この値は0以上360未満の範囲を取ります。0は北、90は東、180は南、270は西です。この値を使うことで、方位だけなら磁気センサーの値を計算せずとも簡単に取ることが出来ます。傾きはpitchとrollのみで判断します。多分以前よりも簡単です。ロールピッチヨーの3つの値の組み合わせだと、どのような経緯を経て、今の傾きになったのかを追跡することが出来ます。それは確かに必要な局面はあるかもしれませんが、通常はその時点の傾きのスナップショットのみが欲しいことが殆どでしょうから、pitchとrollだけでも判断できますし、そのように判断した方が簡単だと思います。でも、加速度センサーの値から傾きは判断できますので、(方位は楽かもですが)傾度センサーの値は別に要らないかなあ、とも思います。


サンプル:
先日、Android SDK WGで、加速度センサーを使った簡単なサンプルを皆で作って遊びました。

作ったアプリの仕様は

・端末を振っているときに、背景の色を変えてください
・振るのを止めたら、背景の色を元(黒)に戻してください
・端末の傾きに応じて、画面に(縦、横、水平などの)文字を表示してください


という感じです。実装したアプリは、例えばこんな動きをします。

多分実装の方法は無数に考えられるのですが、Activityのひとつの実装例を丸ごと載せておきます。

package net.grandnature.android.sdkwg.examples.sensor;

import java.text.DecimalFormat;

import android.app.Activity;
import android.graphics.Color;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class Main extends Activity implements SensorListener {
    View layout;
    TextView accelerometerValue;
    TextView orientationValue;
    TextView filteredAccelerationValue;
    TextView filteredOrientationValue;
    TextView orientation;
    SensorManager sensorManager;
    static DecimalFormat format;
    static {
        format = new DecimalFormat();
        format.applyLocalizedPattern("#0.000");
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // --- views
        setContentView(R.layout.main);
        layout = findViewById(R.id.layout);
        accelerometerValue = (TextView)findViewById(R.id.accelerometer_value);
        orientationValue = (TextView)findViewById(R.id.orientation_value);
        filteredAccelerationValue = (TextView)findViewById(R.id.filtered_acceleration_value);
        filteredOrientationValue = (TextView)findViewById(R.id.filtered_orientation_value);
        orientation = (TextView)findViewById(R.id.orientation);
        // --- sensors
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    }
    @Override
    protected void onStop() {
        sensorManager.unregisterListener(this);
        super.onStop();
    }
    @Override
    protected void onResume() {
        super.onResume();
        sensorManager.registerListener(this, 
                SensorManager.SENSOR_ACCELEROMETER | 
                SensorManager.SENSOR_ORIENTATION,
                SensorManager.SENSOR_DELAY_FASTEST);
    }
    private float[] currentOrientationValues = {0.0f, 0.0f, 0.0f};
    private float[] currentAccelerationValues = {0.0f, 0.0f, 0.0f};
    public void onSensorChanged(int sensor, float[] values) {
        switch(sensor) {
        case SensorManager.SENSOR_ACCELEROMETER:
            accelerometerValue.setText(convertFloatsToString(values));
            // 傾き(ハイカット)
            currentOrientationValues[0] = values[0] * 0.1f + currentOrientationValues[0] * (1.0f - 0.1f);
            currentOrientationValues[1] = values[1] * 0.1f + currentOrientationValues[1] * (1.0f - 0.1f);
            currentOrientationValues[2] = values[2] * 0.1f + currentOrientationValues[2] * (1.0f - 0.1f);
            // 加速度(ローカット)
            currentAccelerationValues[0] = values[0] - currentOrientationValues[0];
            currentAccelerationValues[1] = values[1] - currentOrientationValues[1];
            currentAccelerationValues[2] = values[2] - currentOrientationValues[2];
            filteredAccelerationValue.setText(convertFloatsToString(currentAccelerationValues));
            filteredOrientationValue.setText(convertFloatsToString(currentOrientationValues));
            // 振ってる? 絶対値(あるいは2乗の平方根)の合計がいくつ以上か?
            // 実装例
            float targetValue = 
                Math.abs(currentAccelerationValues[0]) + 
                Math.abs(currentAccelerationValues[1]) +
                Math.abs(currentAccelerationValues[2]);
            if(targetValue > 22.0f) 
                layout.setBackgroundColor(Color.YELLOW);
            else if(targetValue < 10.0f) 
                layout.setBackgroundColor(Color.BLACK);
            // かたむきは?3つの絶対値(あるいは2乗の平方根)のうちどれがいちばんでかいか?
            // 実装例
            if(Math.abs(currentOrientationValues[0]) > 7.0f) {
                orientation.setText("横");
            } else if(Math.abs(currentOrientationValues[1]) > 7.0f) {
                orientation.setText("縦");
            } else if(Math.abs(currentOrientationValues[2]) > 7.0f) {
                orientation.setText("水平");
            } else {
                orientation.setText("");
            }
            break;
        case SensorManager.SENSOR_ORIENTATION:
            orientationValue.setText(convertFloatsToString(values));
            break;
        default:
        }
    }
    private String convertFloatsToString(float[] values) {
        return 
        String.valueOf(format.format(values[0])) + ", " + 
        String.valueOf(format.format(values[1])) + ", " + 
        String.valueOf(format.format(values[2]));
        
    }
    public void onAccuracyChanged(int sensor, int accuracy) {
        
    }

}

サンプルのEclipseプロジェクト:
Eclipseのプロジェクトごと圧縮してここに置いています。
SensorExample.zip

2009年02月19日

Software Design3月号

ついに来ましたよこの日が。
日本Androidの会で、技術評論社さんの雑誌「Software Design」(3月号)の特集を担当しました。
この特集記事は、ようやく来たと言う感じです。全体が網羅されており、Androidの開発を始めようという方にはすごくいい特集です。こういう日が来るのを待ち望んでいましたが、ついに実現しました。

私も2章を書きました。個人的には結構気を遣った力作です。(ミスったところもありますが。)
ぜひ読んでみて下さい。御意見ご感想などありましたら、頂けると嬉しいです。

Software Design (ソフトウエア デザイン) 2009年 03月号 [雑誌]Software Design (ソフトウエア デザイン) 2009年 03月号 [雑誌]

技術評論社 2009-02-18
売り上げランキング :

Amazonで詳しく見る

あ、あと入籍しました。ついに既婚者です。最近いいことたくさんあります。いいですねえ。

2009年02月23日

Android Hackathon

Android Hackathon 3月19日、20日に開催

3/19(木)、3/20(金)に、主催:Android-SDK-Japan、後援:GoogleでAndroidのHackathonがあります。
場所は、セルリアンタワーのGoogle Japanです。
Hackathonというのは、何人かのグループで実際に動くものを開発してみるイベントです。

http://groups.google.co.jp/group/android-sdk-japan/browse_thread/thread/fef47f02acedac50

私もサポートメンバーで参加します。当日会ったら声をかけてください!


2009年02月26日

3/3(火)豆ナイト OSGi

豆蔵という会社で、「豆ナイト」という夜間無料勉強会をやっています。次回は3/3(火)です。テーマはOSGiです。もしご興味があるようでしたら、ご参加下さい。
私も5分ほどLTをする予定になっています。「OSGiとAndroidの比較」という大それたタイトルで話すことになっているのですが、このふたつは比較出来るようなものではないかもしれないので、Androidの紹介だけでも出来ればいいかなと思っています。
詳細はこちら:
http://www.mamezou.com/event/es20090303osgi.html

以下引用です。

タイトル
 灯りを点けましょOSGiに ~ 初めてのバンドル作り ~

日時
 2009年3月3日(火) 19:00~21:00

内容
OSGi Service Platform (以下「OSGiフレームワーク」と記します)は、システムの比較的下回りに近い部分に位置するJava実行環境用のフレームワークです。バージョン3.0以降のEclipseに組み込まれたりしているため広く名前が知られるようになってきましたが、具体的にどんな機能を提供してくれるモノか把握している人(ましてや、それを直接利用したプログラミングをした事がある人)はそれほど多くないのではないでしょうか?

間接的に多くの人達に使われ、名前が広く知られつつあるのに、その正体があまり知られていない...。なんだかネッシーやツチノコのようなフレームワークです。

組込み系システムのプラットフォーム環境としても、携帯電話機や車載器に対してインターネット経由でサーバーからバンドルの配布やリモート制御、デバイスの構成管理等がセキュリティを満たした上で動的に行える可能性があるとして、期待されています。

OSGiフレームワークはJava実行環境の弱点とも言えるモジュール化機能を強力に支援してくれます。バンドルと呼ばれるモジュール単位でJavaのクラス・ファイルやその他のリソース・ファイル群などをアーカイブし、バンドル単位での動的なロード/アンロードを可能にしたり、JavaVMを再起動しないで(つまりアプリケーションを動かしたままで)一部のモジュールを最新バージョンに置き換えたり...といったことが比較的容易にできるようになります。

「比較的容易に」とは言っても、OSGiフレームワークの特性を活かした疎結合でダイナミックなモジュール構造を設計するためには、その基本的な仕組みやいくつかの留意点をしっかりと把握しておくことが必要です。

今回の豆ナイトでは、Eclipseに組み込まれているEquinoxと呼ばれるOSGiフレームワークの実装系を使って、ステップ・バイ・ステップで実際に簡単なバンドルを作っていくという実習を行います。これによって、OSGiフレームワークの基本的な考え方と具体的な利用方法、作法の良いバンドルの作り方などを習得していきます。

About 2009年02月

2009年02月にブログ「GrandNature」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2009年01月です。

次のアーカイブは2009年03月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。