ラベル Android の投稿を表示しています。 すべての投稿を表示
ラベル Android の投稿を表示しています。 すべての投稿を表示

2011年8月16日

Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法

Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!


前回までで思考ルーチンがそこそこ動く様になってゲームとしては結構遊べる状態になったので、今回からは少しずつグラフィックを改善して行きたい。

ひとまずこんな感じで、ゲーム終了時の結果表示の文字列をぐるぐる回してみた。
http://www.youtube.com/watch?v=AYld0bnjxeI

FrameLayoutを使ってオーバーレイ表示


前回まではカスタムのReversiViewの中でdrawTextを使って勝ち負けを表示していた。今回はその代わりにAndroid標準のアニメーション機能を使いたいので、ReversiViewの中心に重なる様にTextViewを配置して、そこに文字列を表示した。

複数のViewを重なる様に配置するには、FrameLayoutを使うと良いらしい。

TextViewには標準でドロップシャドウの機能が付いているのでこれも利用した。


文字列を画面の中心に配置するのは、

 android:layout_gravity="center"

とするだけで出来た。素晴らしい。^^

ちなみにTextViewの上のViewは、ゲーム終了時に画面の背景を暗くするアニメーションを入れる為に使っている。




AnimationをXMLファイルで定義


AnimationをXMLファイルで定義するには、res/の下にanimフォルダを作ってそこに適当な名前でXMLファイルを作成する。

色々試行錯誤して見た結果、こんな形になった。
回転しながらズームイン&フェードインして、一瞬止まった後でスーっと上に動く感じ、が作りたかったので。

また文字列を回すのと同時に、背景を少しずつ暗くするのもalphaアニメーションを使って行なっている。

あと、ちょっとハマったのは、setタグの属性として「xmlns:android=...」を付けないと set全体にfillEnabledとfillAfterの属性を付けられなかった点だった。




AnimationをTextViewに適用


ActivityのonCreateでAnimationUtils.loadAnimation()を呼んで上で定義したアニメーションを準備しておく。

実際にアニメーションを開始するには、TextViewのstartAnimation()を呼ぶだけだ。





ソースコードと次回の予定


現時点のプロジェクト全体のソースコードはこちら。
mikehibm/MiReversi at ver4 - GitHub

次回はいよいよコマをひっくり返す時のアニメーションを作りたい。それが出来たら一応完成かな?


Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!








 .

2011年8月3日

Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装

Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!


前回の記事からあっと言う間に半月以上も経ってしまった。

その間、会社のXenServerがトラブったり、オフィスの引越し準備と大きなプロジェクトのカットオーバーが重なったりで、7月はなかなか大変な月だった。

そんな中で実はオセロの思考ルーチンを少しずつ実装したりもしていた。結局、仕事と子育てで一杯一杯なのはいつもの事なので、早朝5時起きしてでもなんとか自分の時間を作ってやって行くしかないかなあと思う。



思考ルーチンの実装


オセロの思考ルーチンを作り込む事は、このアプリを作り始めた時の「Androidでの2Dグラフィックス処理の習得」という目標からは完全に脱線している。でもどうしても誘惑に負けて、作り込まずにはいられなかった。(笑) 面白いのだから仕方がない。

下の2つのリンク先にはとてもとてもお世話になった。
-鶯教-コンピュータ・リバーシ講座

リバーシ(オセロ)プログラム (2018年現在リンク切れ)

今のところ、作成した思考ルーチン(?)は下の4種類。それぞれが ComputerPlayer クラスのサブクラスになっている。

(クラス名クリックでソースコードを表示します)
ComputerPlayer0 何も考えずに打てる場所の中からランダムに選んで打つ。
ComputerPlayer1 次に打つ場所の候補のリストを、事前に定義された評価テーブルにしたがってソートした上で、評価の最も高い場所に打つ。打った結果についての先読みはしない。
ComputerPlayer2 1手先までの局面の評価値を計算し、最も評価の高い手を選ぶ。
ComputerPlayer3 「ネガマックス法」にしたがって最大4手先までの局面の評価値を計算し、最も評価の高い手を選ぶ。


クラスの継承関係はこんな感じになっている。
  Player (人間とコンピュータの両方に共通のコードを記述)
     HumanPlayer   
     ComputerPlayer (スレッドの起動・停止など全ての思考ルーチンに共通する処理を記述)
       ComputerPlayer0 (各思考ルーチンの実装はこのレベルのクラスに記述)
       ComputerPlayer1
       ComputerPlayer2
       ComputerPlayer3

ゲーム実行時の基本的な流れとしては、HumanPlayerの番になるとボード上の有効な場所がタッチされるまで待ち、ComputerPlayerの番になると別スレッドを起動して思考ルーチンを走らせる様になっている。

思考ルーチンは戻り値としてPointを返すので、あとは人間がその座標をタッチした場合と同様の処理を行えばよい。

ただ、思考ルーチンが走っている間に電話がかかって来たり別のアプリが前面に呼び出されたりするとActivityが終了してしまう可能性がある。その場合は思考ルーチンのスレッドもきちんと終了させる必要があるので、その辺りにはちょっと気を使わないと行けない。



気になる強さは?


ComputerPlayer1と2はそれなりに遊べるとは思うけど、かなり弱い。

ComputerPlayer3は、個人的にはそこそこ強いのではないかと思う。ただめちゃくちゃ強いというほどでもない。自分で対戦して見た限りだと、5回やると4回は自分が負ける。1回ぐらいはぎりぎり勝てるという感じだ。

不思議な事に、こいつに自分が負けるとなぜか嬉しい。早起きして眠い目をこすりながらデバッグした甲斐があったというものだ。(笑)



ソースコードと次回の予定


現時点のプロジェクト全体のソースコードはこちら。
mikehibm/MiReversi at ver3 - GitHub

次回はいよいよ石を裏返す時のアニメーションとか、ゲーム終了時の結果表示画面などをグラフィック機能を駆使して作り込んでみたい。早寝早起きして頑張ろう。^^


Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!





2012/1/12 追記:
ここにある思考ルーチンは最強かも!
Booby Reversi Download Page

強いオセロプログラムについて







2011年7月14日

Androidでオセロゲームを作ってみる (2) ゲームロジックの実装

Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!


前回はひとまずカスタムViewを使ってボードを描いて、画面をタッチした時に石を配置する所まで出来た。

今回はゲームのロジックを実装して実際に遊べる所まで一通り作ってみた。

必要な処理は次の4つになる。
  1. 画面タッチ時に、押された場所が石を配置しても良い場所かどうか判断する。
  2. 挟んだ相手の石を自分の石で置き換える。
  3. 配置可能な場所が無い場合はスキップする。
  4. 両者とも配置可能な場所が無い場合はゲーム終了とする。
これに加えて、「次に配置可能な場所」を示してくれるヒント機能も付けて見た。

小さな丸で表示されているのが「次に配置可能な場所」。
濃い色にすると見辛くなるのでかなり薄くしてある。


ゲーム終了時の結果表示。とりあえずToastでさらっと。


メニューは「設定」、「カウント」、「最初から始める」、「閉じる」の4つ。
設定でヒント表示のON/OFFを変更出来る。


画面を縦表示に固定


マーケットで配布されているオセロゲームをいくつか試した限りでは、全て縦表示で固定されていたので、同じように縦表示で固定する事にした。これはマニフェストファイルでの設定だけなので簡単だ。
Activityタグの属性として、
android:screenOrientation="portrait"
を入れておけばOK。

画面が切り替わる時に盤面の状態を保存


何もしないと、一旦他の画面に切り替わった後で再表示される場合に画面が初期状態に戻ってしまう。

なので盤面の状態を保存しないと行けない。

そのために、Boardクラスに getStateString というメソッドを作った。ここから各セルのステータスを1文字で表したものを連結した64文字からなる文字列を返す様にする。さらに現在どちらの番なのかを表す文字を先頭に付加するので、実際には1+64=65文字になる。

本当の所は、セルの状態は「黒/白/空白」の3つしか無いので情報としては2ビットあれば足りる。そう考えると2bit x 64セル / 8 + 1 = 17という事で、17バイトのバイト配列に格納する事も出来る。ただビット演算などが面倒なので今はこのまま65文字の文字列に格納する事にする。

最初は、この文字列を ViewのonSaveInstanceStateで状態を保存して、onRestoreInstanceStateで復元していた。

ただこれだとひとつ問題がある。端末の「戻る」ボタンで画面を閉じた時にはどちらのメソッドも呼ばれず、状態が失われてしまうのだ。

なのでこれらのメソッドを使わずに代わりにActivityのonPauseとonResumeを使う様に変更した。この場合はデータの保存先はプログラムで適当な所を選ぶ必要がある。今回は保存したいデータが65文字の文字列なので、SharedPreferenceを使う事にした。

これで「戻る」ボタンが押されてもちゃんと状態が保存される様になった。




Viewの invalidate メソッドで必要な部分のみを再描画


画面がタッチされて石を配置する時に BoardクラスのchangeCellメソッドを呼んでいる。このメソッドはこの配置によって変更された全てのセルをArrayListに入れて返してくれる。View側ではこれを受け取って、順に変更されたセルの領域を指定しながらViewのinvalidateメソッドを呼んでいる。

invalidateを引数なしで呼んだ場合はView全体が再描画されるが、引数rectに領域を指定して呼び出すと、その領域だけが再描画される。もちろん出来るだけ再描画する量を減らした方が処理が速くなる。



drawTextで文字を表示


画面の下の部分に現在どちらの番なのかが分かるように drawRoundRectメソッドで四角の枠を表示した。それから、黒と白それぞれの現在の数をdrawTextメソッドで表示する様にした。

さて、これらの座標やフォントサイズの指定方法に少し悩んだ。

基本的には、何も考えずに数値を指定すれば Android側がDIPというデバイス非依存な単位として扱ってくれるらしい。だから画面の物理的な大きさや解像度が変わってもプログラム側は何もしなくてもいいはずだ。

でもこのページを読むと、なにやらXperiaでちょっとした問題があるとの事。
Y.A.M の 雑記帳: Android multi screen 対応
ただし、Xperia では anyDensity = false にすると、drawable-hdpi ではなく drawable-mdpi に格納した画像が使われるという問題があります。

ということで、私は anyDensity = true にして、XML側で sp や dip でサイズ指定し、コード側で指定しなければならない場合は、XMLで dimension を設定して、それを getDimension してsetWidth などに渡しています。
という事なので、yanzmさんの真似をしてXMLファイルに座標関係の値を全て書き出す事にした。

XMLファイルの書き方とプログラムでの使い方の説明はこちら。
Y.A.M の 雑記帳: Android Dimension 単位

いちいちXMLを編集するのは最初はちょっと面倒だけれども、慣れてしまえば逆に微妙な調整などはこの方がやり易いかも知れない。

デバイス間の解像度の違いについては、実機(800x480)とエミュレータ(480x320)で試して見たところ上手く表示されているので、多分大丈夫。


ゲームロジックの実装


Cellクラスで表されている各セルは「今このセルに石が配置されたらどのセルとどのセルが裏返されるか」という「裏返し対象セル」のリスト(mReversibleCells)を持っている。手番が変わるごとに毎回全てのセルについてこれを計算し直している。

このリストのサイズが1以上ある場合は、そのセルは次に石を配置可能な場所であるという事になる。サイズが0なら、タッチされても何も起こらない。

リストのサイズが1以上ある(配置可能な)セルがタッチされた場合は、この前もって計算された「裏返し対象リスト」に格納されているセルのステータスを順に現在の手番を表すステータス(黒または白)に置き換えて行く。

もし配置可能なセルが一つも存在しない状態になった場合は、その手番はスキップ(パス)される。




今回はここまで


さて、これで一応ゲーム開始から終了まで動作する状態になった。

でもここまで来ると、もう少し完成度を上げて見たくなる。やっぱり、石はきれいな画像で表示したいし、裏返す時のアニメーション効果とコンピュータとの対戦機能ぐらいは欲しい。

どこかにそこそこ強いオセロの思考ルーチンのコードが公開されていないものだろうか。探して見よう。(笑)

ここまでのプロジェクト全体のダウンロードはこちらから。
mikehibm/MiReversi at ver2 - GitHub


Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!









 

2011年6月29日

Androidでオセロゲームを作ってみる (1)

Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!


Androidアプリで2Dグラフィックスを扱う練習をしようと思う。

とりあえず簡単なものという事で、オセロ(リバーシ)ゲームを作りたい。

まずはViewを使って描画するか、SurfaceViewを使って描画するかを決めないと行けない。
GLSurfaceViewというのもあるけれどもこちらは今やろうとしている事にはオーバースペックなので置いておく。

SurfaceViewならAndroidで高速描画ゲームが作れる (1/3) - @IT

【libro】 Google androidプログラミング入門/SurfaceViewによる高速描画/SurfaceViewとは?

Graphics の基本

グラフィックス(1)-Viewクラスへの描画 - 愚鈍人

KENSINのホームページ-Androidで星が流れるプログラムを作成してみる

Viewを使った場合は大体4fpsぐらいしか出ないが、SurfaceViewだと30fpsぐらいは出るみたいだ。この差は大きい。

でも「チェスなどのボードゲームならViewで充分」という記述もよく見かけるので、今回はとりあえずViewで行こうと思う。どうしても必要なら後からSurfaceViewに変更するという事で。

まずはこんな感じで初期状態を描画してみた。それなりに見える。(笑)

横向きの場合はこんな感じになる。




カスタムビューによるグラフィック表示


Activityクラスではカスタムビューをセットするだけ。


ReversiViewというカスタムビューを作ってこの中でオセロの表示処理を全て行う。


onDrawから呼ばれるdrawBoardの中で
Paint paint = new Paint();
としているのはパフォーマンス上は良くないらしい。後でちゃんと直そうと思う。

色の指定なども本当はXMLファイルに出してしまいたいところ。

あと、円を描く前にアンチエイリアスを指定しないとギザギザになってしまうので注意。


モデルとビューの分離


オセロの盤面の状態を保持するのと、ゲームのロジックを記述するために2つのクラスを作成しておこう。盤面全体を表すBoardクラスと、個々のマスを表すCellクラスがあれば良いと思う。

Boardクラスにはwidth, height, top, leftプロパティと、Cellクラスのオブジェクトを格納する2次元配列を用意しておく。

Cellクラスには、width, height, top, leftプロパティと、そのマスがどういう状態にあるかを示す変数を作成しておこう。各マスは、「何もない」「黒」「白」の3つの状態を持つので、それらを表すEnum型も定義する。

Boardクラスのソースコードはこちら。

Cellクラスのソースコードはこちら。


とりあえずここまで


ここまでで、画面をタッチすればそこに黒または白の石を置く事が出来る様になった。

一応、既に石が置かれているマスをタッチしても反応しない様になっている。でもまだゲームのロジックと言えるものは何も書いていないので、オセロのルールに関係無く自由に石を置けてしまうし、挟んでも挟まれても何も起こらない。

しかし、これだけでも「一から自分で作っている」という感じがたまらない。(笑)

プロジェクト全体のダウンロードはこちらから。
mikehibm/MiReversi at master - GitHub


Androidでオセロゲームを作ってみる (1)
Androidでオセロゲームを作ってみる (2) ゲームロジックの実装
Androidでオセロゲームを作ってみる (3) 思考ルーチンの実装
Androidでオセロゲームを作ってみる (4) 文字列をぐるぐる回す方法
Androidでオセロゲームを作ってみる (5) 裏返しアニメーションを付けてついに完成!







2011年6月16日

AndroidでURLを開く度に自作のアプリを起動する (5) 時間のかかる処理を別スレッドで実行する



HTTP通信を別スレッド化する


Androidアプリケーションでスレッドを使う方法はいろいろあるみたいだが、今回は以下の手順で実装した。
  1. ActivityクラスでRunnableインターフェースをインプリメントする。
  2. android.os.HandlerクラスのインスタンスをActivityクラスで保持する。
  3. Runnableのrunメソッドをオーバーライドする。
  4. 別スレッドでの処理を開始する。


1. ActivityクラスでRunnableインターフェースをインプリメントする。

Activityでインプリメントせずに、単にその都度Runnableのインスタンスを生成しても多分大丈夫。ただこちらの方が多少はメモリの節約になるらしい。(という記述をどこかで読んだ気がする。)
public class IntentReceiveActivity extends Activity implements Runnable {
  ...
}

2. android.os.HandlerクラスのインスタンスをActivityクラスで保持する。

別スレッドでの処理が完了した後にUIスレッド側で実行する処理を受け取る為に必要になる。
private Handler mHandler = new Handler();

3. RunnableのrunメソッドをOverrideする。

別スレッドでの処理が完了した後、mHandlerのpost()メソッドを呼んでUIスレッド側で実行したい処理を渡す。
変数urlとtitleはfinalで宣言しないとハンドラ内から参照する事が出来ないので注意。でも逆に言うとfinalで宣言したローカル変数がそのまま参照出来るので、Bundleに詰め込んでパラメータとして渡すなどという方法と比べるとこの書き方はかなり便利だと思う。
@Override
 public void run() {
  //HTTP通信を実行してページのタイトルを取得
  final String url = getIntent().getDataString();
  final String title = HttpUtil.getTitle(url, getString(R.string.msg_no_title));

  //処理完了後、ハンドラにUIスレッド側で実行する処理を渡す。
  mHandler.post(new Runnable(){
   @Override
   public void run(){
    try {
     //ListViewを更新(該当のurlが既にListViewから削除されていた場合はfalseを返す)
     if (updateList(url, title)){
      //データベースを更新。
      HistoryDb.save(url, title);
     }
    } catch (Exception e) {
        showErrorDialog(e);
    }
   }
  });
 }

4. 別スレッドでの処理を開始する。
new Thread(this).start();
実行時のイメージはこんな感じになった。処理中はくるくる回るアイコンを表示しておくと結構いい感じ。

HTTP通信処理中の表示(一番上の行)


複数のスレッドが同時に走っているのが分かる

処理中とそうでない時の表示の切り替えは ListItemAdapter クラスの中でtitleがセットされているかどうかで判断して行っている。その部分のソースは こちら。



ネットワーク接続が可能かどうか事前に調べる


ネットワークに接続されていない場合はタイトルの取得を行わずにURLだけを一覧に追加する様にした。接続されているかどうかの判断は下の関数で可能だ。

private boolean isConnected(){
  ConnectivityManager cm = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
  NetworkInfo info = cm.getActiveNetworkInfo();
  if (info == null){
   return false;
  } else {
   if (info.isConnected()){
    return true;
   } else {
    return false;
   }
  }
 }
AndroidSDK開発のレシピ―104個のレシピで学ぶAndroidアプリ開発の極意 塚田 翔也 (著)より



ブラウザを自動的に開くかどうかを設定で選べる様にする


設定画面にチェックボタンを追加して、インテントで受け取ったURLを自動的にブラウザで開くかどうか設定出来る様にした。ONの場合とOFFの場合で試してみた所、自動的にブラウザを開かずに単に一覧リストを表示するだけの方が使いやすい様な気がする。




リダイレクト時に複数のエントリが追加されてしまう問題


自動的にブラウザを開く場合にWebサーバーからリダイレクトのレスポンスが返って来ると、標準ブラウザはリダイレクト先のURLを開く為にまたインテントを発行する様になっている。そのインテントを再度このアプリが受け取ると、結局一覧リスト上はURLが微妙に違うだけで内容は同じページ、という行が複数追加されてしまう事になる。

例えば、
http://d.hatena.ne.jp/hogehoge/
というサイトを開いて、
http://d.hatena.ne.jp/hogehoge/touch
にリダイレクトされた場合にこの現象が起きる。

これを防ごうとすると、

「インテントを受け取った時にそれがリダイレクトされて来たものなのかどうか判定してそうであれば無視する。」

という動作が出来ればいいのだが、その判定方法が見つからないのでこの方法は今の所実現出来ていない。

次善の策としては、

「時間制限を設けて、前のインテント処理から一定の時間内(20秒とか)は同じタイトルの行を追加しない様にする。」

というやり方が考えられる。

ただ、ややこしくなりそうなのでそこまで実装するのは今回は見送り。(笑)



全ソースと次の目標


今回の全ソースはこちらからダウンロード可能になっている。
mikehibm/android-browser-intent03 at intent04 - GitHub

他にも「設定画面で標準以外のブラウザを選べる様にする」とか、「ListViewにチェックボックスを付けて処理対象を選択出来るようにする」とか、検討した機能はあった。

「メールを送る代わりにどこかのサーバーにデータを送信する」というのも面白そうだ。Google DocumentsのSpreadsheetに書き込めば意外と使い途があるかも知れない。

ただこのアプリを変更するのは今回で一旦ストップしておこうと思う。

実を言うと、「気になったリンクの一覧を簡単にPCに送る」というもともとの目的は Google Readerの「スターを付ける」機能を使えばほぼ達成出来る事に気付いてしまったからだ。
Googleリーダーに「後で読む」っぽい機能をつける方法 : ライフハッカー[日本版]
情報の収集、閲覧、集約は全てGoogleリーダーに任せることにした。 - iPhoneとiMacと自分と…

「スターを付ける」を使い出してからは、この自作アプリを使うのはTwitterなどGoogle Readerの管理外のURLをPCに送りたい時だけになってしまった。まあそれでも完全に用が無くなった訳ではないのでよしとしよう。


それに、他にもっと面白そうな事がたくさんある。あり過ぎて困る。

次からはSurfaceViewを使ったグラフィック処理や、SoundPoolを使って音を出す事などをやってみようかなと思っている。

あと、OpenGLにも興味を惹かれる。何か子供向けの教育アプリでも作れないものかなとこんな本を読んでみたりもしている。
OpenGLで作る Android SDKゲームプログラミング 中島 安彦 (著), 横江 宗太 (著), 株式会社パンカク (著)
簡単なものから始めて少しずつ機能を追加して行く構成になっていて、とても分りやすい。


AndroidでURLを開く度に自作のアプリを起動する







2011年6月8日

米国版HTC EvoがGingerbreadにアップデートされて変わった点とは

6月3日からGingerbread(Android 2.3.3)への公式アップデートが可能になったので、早速やって見た。

Androidのバージョンがほぼ最新に近い2.3.3になるという事で、事前の情報だとこんな感じで色々と変わるみたいだった。
Nexus Sを2日使った感想(ソフトウェア編) | juggly.cn
Android 2.3「Gingerbread」スクリーンショットツアー~待望のあんな機能やこんな機能が! : ライフハッカー[日本版]

さて、ワクワクしながら実際にアップデートしてみた所、見た目はHTCのSense UI(以前と同じバージョン)のままなのでほとんど違いに気付かない。これには正直がっかり。


Sense UIが悪い訳ではないけれど、やっぱり新しいもの好きなので少しは見た目も変わって欲しかった。 まあ別のHomeアプリを入れればいくらでも変えられるので問題は無いけど。

Androidのバージョンはこの通り、確かに2.3.3に変わっている。




バッテリー使用状況のグラフ表示

個人的には一番目立った変更点はこれだった。


バッテリーの使用状況が分かり易くなったのでこれは確かに有り難い。



全体的な動作の改善

確かに個々の動作が前よりもキビキビとしている様に感じる。もしかすると気のせいかも知れないというレベルだけれども、なんとなく以前よりも動作が軽くなった様に思える。


と、実は今日まで普通に使っている分にはこれぐらいしか違いに気付かなかった。 以下、上のリンクの記事などを見ながら細かく変更点をチェックした。



SIP通話に標準対応

Menu→Settings→Callを開くと一番下に「Use Internet calling」というオプションが追加されている。ここでWifi接続時にSIP通話機能を使うかどうかを設定出来る(中央の画像)。 またその上の「Accounts」を開くと、SIPアカウントを複数追加する事が出来る様になっている(右の画像)。

試しに以前に構築したテスト用のAsteriskサーバーに接続して発信した所、問題無く使えた。これは社内にSIPのVoIP環境がある所ではものすごく有用な機能ではないだろうか。Android携帯さえあれば内線の受信・発信も出来る様になるからだ。もちろん今までもSIP電話アプリを入れれば出来た事だが、OSの標準機能として統合されているという事で、さらに安心して使える様になりそうだ。


但し自宅のWifi接続を使って会社のLANにVPN接続してからSIPで発信しようとすると出来なかった。 VPN経由はダメみたいだ。




テキストのコピー&ペースト

テキストを選択する際の「つまみ」の画像などはSense UIの関係か標準とは違う見た目になっている。 以前より多少使い易くはなったかも知れない。

でもペーストする時に勝手に両端に空白を入れられてしまうのはどうも変な感じ。

また、日本語の文字列を選択状態にすると「コピー」の選択肢が出ずに「ペースト」しか選べなくなる場合があった。これはどう見てもバグっぽい。もしかすると使っているIMEとの相性もあるのかも知れないが、よく分からない。



電源ボタン長押しで再起動が可能に

ほとんど使う事はないけども、これ(Restart)も確か以前は無かったような気がする。


そんなこんなで、Gingerbreadにアップデートはされたものの、日常的な使用感という意味ではこれまでとほとんど違いが無い。バッテリーの持ちが特に良くなったという感じも無い。

とりあえず大きな不具合が無さそうだと言う事だけでも有難い、…とポジティブに受け取っておこうと思う。(笑)






.

2011年6月3日

米国でHTC EvoのGingerbreadへのアップデート開始へ

Sprintから公式アナウンスがあった。


Sprint Community: Announcements: HTC EVO 4G from Sprint Gets Android Gingerbread Update

Gingerbreadが公開されたのが2010年の12月6日との事なので、ちょうどその半年後にEvoに反映される事になる。

よし、いよいよ来週からGingerbreadだー。


この際だからFroyo(Android 2.2)とGingerbread(Android 2.3)の違いを予習しておこう。

Android 2.3 Gingerbreadの新機能のまとめ | Macin' Blog

【レビュー】Google携帯「Nexus S」の実力は? - 【後編】Androidの"買い時"到来 (1) Gingerbreadで何が変わったのか | 携帯 | マイコミジャーナル

#android しづ子ファッション通信 Σ(^A^;) : で結局 gingerbread って何が変わったの? - Froyo から変化しているUIを集めてみた

Android 2.3(Gingerbread)における新機能、変更点のメモ | juggly.cn

Difference Between Android 2.2 (Froyo) and Android 2.3 (Gingerbread)

Android 2.2 (Froyo) Vs 2.3 (Gingerbread)

う~ん、NFC非搭載の端末では正直言ってあんまり劇的に感動させられる様な違いは無いのかも知れない。

文章に表せない点で使い勝手が良くなっている事に期待したい。あとはVoIPに標準対応という辺りが面白そうかな。






 

2011年6月1日

AndroidでURLを開く度に自作のアプリを起動する (4) HTTP通信を行ってタイトルを取得する



前回まででSQLiteデータベースに履歴一覧を保存出来る様になり、少しは本格的(?)にアプリらしくなって来た。

今回はURLからWebページのタイトルを取得して表示する様に変更したい。

スレッドを使って非同期にHTTP通信を行ない、処理結果をListViewに反映すると言う、このブログの流れからすると今までで最高レベルの難易度(笑)になる内容だ。実際、作っていて「あ、そうだったのか」と気付かされる事が多くとても勉強になった。プログラミングは実際に手を動かして試行錯誤しながら習得するに限ると改めて感じた。


前回までの画面

今回の画面
URLだけでなくページのタイトルも表示
すると一気に実用的(な感じ)になった。
ついでにListViewの各行の背景にグラ
デーションを使ってみた。




URLからHTMLを取得する


まずはHTTP通信でURLが示すWebページにアクセスしてHTML文字列を取得する部分だ。HttpUtilというクラスをプロジェクトに追加して、そこに色々書いて行く事にした。

そのHttpUtilクラスに作ったgetHtmlメソッドはこんな感じになった。

public static String getHtml(String url){
        String result = null;
        
        HttpGet httpGet = new HttpGet(url);
        DefaultHttpClient client = new DefaultHttpClient();
        HttpParams httpParams = client.getParams();
        
        HttpConnectionParams.setConnectionTimeout(httpParams, 1000 * 10);   //接続のタイムアウト(ms)
        HttpConnectionParams.setSoTimeout(httpParams, 1000 * 60);           //データ取得のタイムアウト(ms)
        client.setParams(httpParams);

        try {
            // レスポンスを取得
            HttpResponse httpResponse = client.execute(httpGet);
            int status = httpResponse.getStatusLine().getStatusCode();

            if (HttpStatus.SC_OK == status){
                //Content-Typeを取得
                Header[] headers = httpResponse.getHeaders("Content-Type");
                if (headers.length > 0) { 
                    String contentType = "";
                    contentType = headers[0].getValue();
                    if (contentType.contains("text/html")){

                        //HTMLを取得
                        HttpEntity entity = httpResponse.getEntity();
                        if (entity != null){
                            byte[] arr = EntityUtils.toByteArray(entity);
                            
                            //文字エンコーディングを判定
                            String encoding = detectEncoding(arr);
                            if (encoding == null) encoding = findEncoding(entity, arr);
                            
                            //判定されたエンコーディングでバイト配列から文字列に変換
                            result = new String(arr, encoding);
                            
                            //entityのリソースを解放
                            entity.consumeContent();            
                        }
                    }
                }
            }
        
        } catch (ClientProtocolException e) {
            Log.d(TAG, e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, e.getMessage());
        } finally {
            //HTTPクライアントを終了させる
            client.getConnectionManager().shutdown();
        }
        
        return result;
    }


予想もしなかった部分でつまずいた。文字エンコーディングの判別だった。

SDK標準のDefaultHttpClientクラスが上手く処理してくれるのかと思っていたらそうではなく、取得したバイト配列を文字列に変換する部分は自分でやらないと行けない様だ。

最初は自前でResponseヘッダのContent-Typeを見たりmetaタグのcharsetの値を見たりして、なんとか上手く動くようにはなったのだが、それでもたまに表示したタイトルが文字化けしている事があった。なぜだろうと該当のWebページのソースを表示して見ると、metaタグでの指定はEUCなのに実際にはUTF-8でエンコーディングされている、などという様にResponseでの指定内容と実際のエンコーディングが異なっているケースがある事が分かった。

結局、Responseに含まれているエンコーディング指定は完全には信用出来ないのだ。ガーン!

それで色々調べた挙句、「juniversalchardet.jar」というライブラリを利用させてもらう事にした。
juniversalchardet - Java port of universalchardet - Google Project Hosting

juniversalchardet - Javaについて

このライブラリを使っている部分はこんな感じ。
private static String detectEncoding(byte[] arr){
        byte[] buf = new byte[4096];
        ByteArrayInputStream stream = new ByteArrayInputStream(arr);
        UniversalDetector detector = new UniversalDetector(null);

        try {
            int nread;
            while ((nread = stream.read(buf)) > 0 && !detector.isDone()) {
                detector.handleData(buf, 0, nread);
            }
        } catch (IOException e) {
            Log.d(TAG, e.getMessage());
        }
        detector.dataEnd();
        return detector.getDetectedCharset();
    }


2011/06/02 追記:
下のページではjuniversalchardetについて「デコード時の文字コードがEUC-JPだった場合に、判定に失敗する可能性が非常に高い。」と書かれているので、要注意。
エンコード時の文字コードが不明なURLをJavaでデコード | grush-blog




HTMLからタイトルを抜き出す


HTMLが取得出来たら、次は<title>タグの始まりと終わりを探してその間の文字列を抜き出せばOKだ。

正規表現を使えば楽勝だ、と思っていたら、これも意外と手こずってしまった。

単純に
<title>タイトルの文字列</title>
という形になっていれば問題無いのだが、
<title id='aaa'>タイトルの文字列</title>
などの様に開始タグに属性が付いている事もある。

それに、
<title>(改行)
タイトルの(改行)
文字列(改行)
</title>
みたいにtitleタグの中に改行が入っているケースもある。正規表現は行単位に処理される(と思う)ので、開始タグと終了タグの間に改行が入ってしまうと途端に話がややこしくなる。(多分。)

改行については最初に除去してから処理すればいいかと思ってやってみたけれども、そうすると文字列が長い場合に大量にCPUを使って戻って来なくなったりするので簡単には行かなそうだった。

そんなこんなで結局正規表現を使うのは止めて indexOf を駆使(?)して自前で抜き出す事にした。

本当はDOMを操作するライブラリなどを使えばこの辺を簡単にしてくれるメソッドが用意されているのかも知れない。

実際のタイトル抽出部分のコードがどうなったかは、こちらから参照可能だ。javaの達人であればもっとキレイに書けると思うので、ちょっと恥ずかしい。

それから、HTML文字列に数値文字参照(例:&#65374;→ 「〜」)が含まれている場合や、実体参照(例:&gt;→「>」)が含まれている場合もあったので、タイトルを抜き出した後それぞれ変換処理をかませるようにしておいた。

単にHTMLからタイトルを抜き出すだけの処理なのに、結構色々あるものだ。

とここまで書いて今また一つ気付いてしまった。 コメントアウトされたtitleタグへの対応だ。
<!-- <title>コメントアウトされたタイトル</title>-->
<title>抽出して欲しいタイトル</title>
というパターンになっていると今回のコードだとコメントアウトされた方のタイトルを抜き出してしまう。こう言うケースにも対応しようと思うと、やはり何らかのDOMライブラリを使う方がいいのかも知れない。




全ソースと次の目標


今回のソースでHTTP通信の処理は別スレッドで行う様になっている。Androidプログラミングでは必須とも言えるマルチスレッド化については、長くなりそうなので別のエントリで書こうと思う。

ダウンロードはこちらから。
mikehibm/android-browser-intent03 at intent04 - GitHub


AndroidでURLを開く度に自作のアプリを起動する







.

EclipseでAndroidのプログラムを実行しようとして変なxml.outファイルが出来てしまう場合

EclipseでAndroidの開発をしている時にすごく気になる事があった。

CTRL+F11で「実行」のはずが、何も起こらずに、代わりによく見ると変なファイルが出来ているのだ。

エディタでXMLファイルを開いている場合に発生する。

javaのソースを開いている場合は問題ないので、CTRL+F6を押してjavaのソースファイルを開いた状態にしてから実行する様に気を付けていたのだが、急いでいる時はついつい忘れてしまって、不便な事この上ない。

その解決方法が今日やっと見つかった。
C/J Prog's Blog: EclipseでF11を押すとAndroidManifest.xml.outが生成されるときの対処
- EclipseにADT PluginとWTP Pluginをインストールしている
- Androidプロジェクトで作業している
- xmlファイルを開いている
- (Ctrl+)F11を押す

これはEclipse WTPの機能で、xmlファイルに対して Run As > XSL Transformation を実行したことになるため。

以下の設定を変更すればOK。

Window > Preferences > Run/Debug > Launching を開き、
"Always launch the previously launched application' in the 'Launch Operation' section."
のラジオボタンを選択する。

との事。助かりました。

これですっきりした。ww







.

2011年5月20日

AndroidでURLを開く度に自作のアプリを起動する (3) SQLiteデータベースを使う



先日のBloggerの障害の影響でこの前書いたエントリが消えてしまった。「ほぼ全てのデータが復旧された」との事だったが、僕が先週の金曜日に朝6時に起きて1時間半もかけて書いた文章はとうとう戻って来なかった。(涙)
Googleの「Blogger」が20時間半に及ぶサービス障害、週末にほぼ復旧 - ニュース:ITpro
やっぱりブログ記事もどこかにバックアップしておいた方が良いのだろうか。と言ってもローカルに保存すればハードディスクのクラッシュや記録メディアの紛失が心配だし、別のクラウドサービスに保存するにしても結局データが消えてしまうリスクは無くならない。考え出すと切りがない。。。

気を取り直して、部分的に残っていた下書きからもう一度書き直す事にした。


さて、


前回までで「ブラウザで表示したURLの履歴をメールで送信する」という目的は一応達成出来た。

ただしこの状態だと、何らかの理由でこのアプリのプロセスが終了させられた場合に履歴の一覧が消えてしまうという問題がある。単純に他のアプリに隠れて画面の裏に回っただけではすぐにプロセスが終了する事はないのだけれども、他のアプリがメモリを大量に使用して空きメモリが不足した場合や、単に長い時間プロセスがアクティブにならなかった場合などにシステムの判断で終了させられる事になる。

今回はプロセスが終了しても履歴の一覧を保持出来る様に、SQLiteのデータベースに保存する様に変更したい。

データベースを開く


AndroidでのSQLiteデータベースの扱い方は大体下のリンク先を見れば把握出来る。
Androidアプリのデータ保存方法の一つ「SQLite」の使い方 SQLiteOpenHelper編 | mucchinのAndroid戦記

Androidアプリでのデータベース基礎 ~速習! Androidアプリケーション開発(4)~(1/3):CodeZine

データを簡単に保存する方法(SQLite編) « Tech Booster
データベースを新規に作成するのではなく、予めアプリのリソースとして組み込んでおいたものをコピーする方法はこちら。
Y.A.M の 雑記帳: Android あらかじめ作成した SQLite database をアプリに取り込む

今回は1テーブルしか使わないので、HistoryDbというクラスを作ってその中でデータベースに関する操作をすべて行う事にしようと思う。もし複数のテーブルがある場合は、データベース全体を表すクラスと各テーブルに対応したクラスに分けた方が多分すっきりするだろうと思う。

とりあえずまずは init() メソッドを作って、最初に必ずこれを呼び出してデータベースが無ければ作成する様にした。
public static void init(String pkgName) throws Exception{
    DbPath = "/data/data/" + pkgName + "/" + DBNAME;
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DbPath, null);
    try {
        //テーブルが無ければ作成する。
        createTable(db);
    } catch (Exception e) {
        throw e;
    } finally{
        db.close();
    }
}
 
private static void createTable(SQLiteDatabase db) {
    String sql = "CREATE TABLE IF NOT EXISTS " + TBL_HISTORY
  + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT, title TEXT);";
    db.execSQL(sql);

    //urlにインデックスを追加。
    sql = "CREATE INDEX IF NOT EXISTS 'main'.'ix_history_url' ON 'history' ('url' ASC)";
    db.execSQL(sql);
}



データベースから一覧を取得する


HistoryDbクラスに selectAll() というメソッドを作ってその中で全件を取得する。取得した結果をループして、1レコード分の情報をHistoryDbクラスのインスタンスのプロパティ(正確には手抜きしたのでパブリックなメンバー変数)にセットし、ArrayListに追加して行く。
public static ArrayList selectAll() throws Exception{
    ArrayList array = new ArrayList();
  
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DbPath, null);
    try {
        //全件を取得する。
        String[] columns = {"_id", "url", "title"};
        String where = null;
        String having = null;
        String group_by = null;
        String order_by = "_id DESC";
  
        Cursor cursor = db.query(TBL_HISTORY, columns, where, null, group_by, having, order_by);
        while (cursor.moveToNext()){
            HistoryDb hist = new HistoryDb();
            hist.id = cursor.getInt(cursor.getColumnIndex("_id"));
            hist.url = cursor.getString(cursor.getColumnIndex("url"));
            hist.title = cursor.getString(cursor.getColumnIndex("title"));
            array.add(hist);
        }
    } catch (Exception e) {
        throw e;
    } finally{
        db.close();
    }
    return array;
}


結果のArrayListを受け取ったActivity側では、ArrayListの情報をまたループしてListViewにバインドされた adapter に入れ直している。ちょっと冗長なやり方になってしまった。
//DBから全件取得。
ArrayList array = HistoryDb.selectAll();

//ListViewに表示。
adapter.clear();
for (HistoryDb hist : array) {
    adapter.add(hist.url);
}




データベースに書き込む


HistoryDbクラスに save() メソッドを作って保存処理を書く。既にDBに存在しているかどうかは本来は主キーである「_id」の値で判断するべきだが、今回はURLでも一意になるのでURLを検索キーとして使った。まずUPDATEを実行してその結果が0件であればINSERTを行うというロジックになっている。
public static void save(String url, String title) throws Exception {
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DbPath, null);
    try {
        //同じURLが既にあれば更新する。無ければ挿入する。
        insertOrUpdateHistory(db, url, title);
    } catch (Exception e) {
        throw e;
    } finally{
        db.close();
    }
}
 
private static void insertOrUpdateHistory(SQLiteDatabase db, String url, String title) {
    ContentValues values = new ContentValues();
    values.put("url", url);
    values.put("title", title);

    String[] args = { url };

    //同じURLが既に保存されていれば更新する。
    int n = db.update(TBL_HISTORY, values, "url = ?", args );
    if (n == 0){
        //無ければ挿入する。
        db.insert(TBL_HISTORY, null, values);
    }
}




メニューにアイコンを付ける


メニューにアイコンを付けて見た。すると一気に見栄えが良くなった。アイコン一つで、不思議なものだ。アイコンの指定方法は、メニューを定義しているXMLファイル内のitemタグの属性として、
android:icon="@android:drawable/ic_menu_XXX"
を付けるだけだ。


Androidに標準で含まれているアイコンの一覧は、こちらで調べられる。
Taosoftware: Android メニューアイコン



今回の全ソース


今回の全ソースはこちらからダウンロード出来る様になっている。
mikehibm/android-browser-intent03 - GitHub

さて、ここまででデータベースに履歴一覧を保存出来る様になり、少しはアプリケーションらしくなって来た。

せっかくここまで来たら、一覧にURLだけでなくページのタイトルも表示したくなって来た。タイトルはインテントでは飛んで来ないので、アプリ内でHTTP通信を行って取得する必要がありそうだ。それから、ページがリダイレクトされた場合にリダイレクト元とリダイレクト先の両方のURLが履歴として残ってしまうという問題も、出来ればなんとかしたい。

さて、上手く行くかどうか、それは次回のお楽しみという事で。


AndroidでURLを開く度に自作のアプリを起動する







.

2011年5月10日

AndroidでURLを開く度に自作のアプリを起動する (2) メール送信機能と設定画面の追加



前回はURLを開くインテントを受け取ってListViewに追加した後、標準ブラウザで開くという大まかな流れを作成した。

今回は、ListViewに保持しているURL履歴の一覧をメールで送信するという部分と、その為に必要な設定画面を実装したい。

全ソースがダウンロード出来るリンクを最後に付けておいたので、興味のある方はぜひダウンロードしてあれこれ試してもらえればと思う。




メールを送る


「メールを送る」とは言っても、アプリから直接メールサーバーに接続してデータを送るのは大変だ。やろうと思えばその為のライブラリもある事はあるらしい。
Android: マルチスレッドでJava Mail
Downloads - javamail-android - JavaMail port for the android plateform - Google Project Hosting

が、今回は単純にデフォルトのメーラーを起動する為のインテントを発行するだけにしようと思う。

メーラーを起動する為のインテントは、次の方法で発行出来る。
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:" + to_addr));
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, message );
startActivity(intent);

メニューボタンから「メール送信」を選ぶとListViewの内容をメールで一括送信する様にした。


本当は startActivity の代わりに startActivityForResult を使って実際に送信ボタンが押されたのか、キャンセルされたのかも取得したかったのだが、どうも上手く結果が返って来なかったのでこれは断念した。



設定画面を作る


とりあえず3つの項目を設定出来る様にしておいた。


設定画面の作り方はこちらが参考になった。(特に設定された内容をサマリーに表示する部分)
Y.A.M の 雑記帳: Android 設定画面を作成する
Y.A.M の 雑記帳: Android Preference の summary を動的に変更



ListViewで項目がタップされた時の処理


ListViewで項目がタップ(クリック)された時の処理は、AdapterView.OnItemClickListenerクラスのインスタンスをsetOnItemClickListenerメソッドでListViewにセットすれば記述出来る。

例えばこんな感じになる。
//リストの項目がタップされた時の処理
list.setOnItemClickListener(
    new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
            ListView listview = (ListView)parent;
            selected_url = (String)listview.getItemAtPosition(position);
            dialog.show();
        }
    }
);

今回は項目がタップされたら処理を選択するダイアログを表示する様にしてみた。


この処理は1回だけ実行すれば良いのでActivityのonCreateで行う様にする。
//リストの項目がタップされた時に開くダイアログを準備。
String[] str_items = { getString(R.string.mnu_browser) , 
                       getString(R.string.mnu_send), 
                       getString(R.string.mnu_delete)};
final AlertDialog.Builder dialog = new AlertDialog.Builder(this)
        .setIcon(R.drawable.icon)
        .setTitle(getString(R.string.mnu_select))
        .setItems(str_items, 
            new DialogInterface.OnClickListener(){
                //ダイアログの項目が選択された時の処理。
                public void onClick(DialogInterface dialog, int which) {
                    switch (which){
                        case 0:
                            openBrowser(selected_url);
                            break;
                        case 1:
                            sendEmail(selected_url);
                            break;
                        case 2:
                            deleteUrl(selected_url);
                            break;
                        default:
                            break;
                     }
                  }
              }
        );

参考にしたサイト: 色々なダイアログの例があるので便利。
八角研究所 : Android で再開する Java プログラミング(14) - ダイアログを制するものがAndroidを制する!



今の所の問題点


ここまでで一応URLの履歴をメールで送信出来る様にはなった。

ただ、ちょっと気になる点がある。それはブラウザでリダイレクトが発生する度に新たに「ブラウザで開く」インテントが発生するという事だ。結果として履歴の一覧にはリダイレクト前と後のURLがそれぞれ残る事になる。

例えば、bit.lyなどの短縮URLサービスを使った場合や、スマートフォンからのアクセスを自動的に専用のURLに誘導する様になっているサイトなどでこの現象が起きる。

実質的に同じページを指しているのに履歴一覧に複数行表示されるのは、ちょっと都合が悪い。リダイレクト後のインテントを受け取った時にそれが「リダイレクトされたものである」という事が認識出来ればリストに追加しない様に出来るのだが、今の所その方法を見つけられていない。



今回の全ソース


mikehibm/android-browser-intent02 - GitHub

次回はいよいよSQLiteを使ってローカルデータベースにURLの履歴を保存する様に変更してみたい。

ちなみに、現在大活躍中の参考書はこちら。まだAndroidの世界で右も左も分からない自分には必携の書になっている。
AndroidSDK開発のレシピ―104個のレシピで学ぶAndroidアプリ開発の極意


AndroidでURLを開く度に自作のアプリを起動する






.

2011年5月3日

AndroidでURLを開く度に自作のアプリを起動する (1) インテントを受け取る



最近、ようやく少しずつAndroidのプログラムを作り始めている。

とりあえず必要に迫られて試して見たのが、

「ブラウザで開く」というインテントを受け取ってURLの履歴を保存し、再度そのURLをブラウザで開く

というアプリ。
単純なアプリだが、実際に作って見るとインテントの面白さを実感出来る。



動作の概要


例えば「はてなブックマーク」のアプリからリストの項目をタップすると「ブラウザで開く」インテントが発行される。(もちろんアプリはURLからブラウザを開く事が出来るものであれば何でも良い。)

インテントに対応出来るアプリが複数ある場合は選択ダイアログが表示される。

自作アプリ「intent01」を選択すると、標準ブラウザーが開く。

「戻る」ボタンでブラウザを閉じると自作アプリに戻る。開いたURLの履歴が表示されている。



インテントを受け取る


Androidのインテントには、起動するコンポーネントを指定して発行される明示的インテントと、特に指定しない暗黙的インテントがある。面白いのは暗黙的インテントの場合だ。

暗黙的インテントが発行された場合は、システムがそれを処理出来るアプリを自動的に見つけて起動してくれる。候補となるアプリが複数見つかった場合は、選択する為のダイアログが表示され、ユーザーがどれを起動するか選択出来る仕組みになっている。

つまり自作のアプリを「"ブラウザでURLを開く"インテントの処理が可能です」と宣言しておけば、システムがそれを認識して該当の暗黙的インテントが発行された時に選択ダイアログに表示してくれる訳だ。

この宣言をするには、AndroidManifest.xmlファイルのactivityタグの下に次の記述を追加するだけで良い。

<intent-filter>
    <action android:name="android.intent.action.VIEW"  />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="http" />
    <data android:scheme="https" />
</intent-filter>

アプリ内では、getIntent()でインテントを取得出来る。単にホーム画面のランチャーから起動されたのか、別のアプリで「URLをブラウザで開く」操作をされて暗黙的インテント経由で起動されたのかは、取得したIntentのgetAction()で判断可能だ。


if (Intent.ACTION_VIEW.equals(intent.getAction()) ){
    //暗黙的インテント経由で起動された時の処理
} else {
    //ランチャーから起動された時の処理
}



ListViewに表示する


インテントに詰め込まれたURLは、
String url = intent.getDataString();

で取得出来る。

ここまで来れば、後はListViewにURLの文字列を追加する部分さえ作れば履歴の表示が出来る。

注意が必要だったのは、ListViewに表示するArrayAdapterをstaticで宣言する事。でないと起動される度に毎回newされてしまい、リストには最後の1件しか表示されなくなってしまう。



標準ブラウザでURLを開く


ListViewに追加した後、標準ブラウザでこのURLを開く為には、明示的にブラウザを指定して同じインテントを発行し直せば良い。
intent.setClassName("com.android.browser", "com.android.browser.BrowserActivity");
startActivity(intent);

setClassNameの1つ目の引数はパッケージ名、2つ目はクラス名なので、これらを変えればもちろんFireFoxなど他のブラウザを呼び出す事も可能だ。(オプションで設定可能にしたら便利かも知れない。)

ブラウザからハードウェアのバックボタンで自作アプリの画面に戻るとちゃんとListViewにURLが追加されている。素晴らしい。(笑)

もちろんこのままだとリストの内容はstatic変数で保持しているだけなので、アプリのプロセスが終了した時点できれいさっぱり失われてしまう。本来ならローカルデータベースに保存するなどの処理を追加する必要がある。(それは次回以降のネタとして取って置きたい。)



結局何がしたかったのか


で、実は何がしたかったかと言うと、この履歴一覧の内容をPCに送りたいのだった。

最近寝る前に携帯で気になるニュースやブログ記事をピックアップしておいて、翌日の空き時間にPCでじっくり読むというスタイルが定着して来たので、出来るだけ簡単にURLの一覧をPCに送信する方法はないかと考えていて思いついた方法がこれだ。

もちろん「Read It Later」やその類のアプリもちょっと試しては見たけれども、いちいちユーザーアカウントを作るのも面倒だし、そもそも送信元のアプリから「共有」もしくは「送る」という操作をしないといけないのが面倒に感じていたのだ。例えば、「Google Reader」からだと一覧の記事を長押ししてもそこには「送る」メニューは無く、記事の詳細を開いた上でメニューボタンを押して、「その他」から「Send」を選んで、それからようやく送り先のアプリを選べる事になる。

記事のタイトルだけチェックしてさくさくとPCに送りたいと思うと、いちいち「送る」メニューを選ぶのはどうも使い辛い。

今回の自作アプリだと、一度デフォルトブラウザとして選択しておけば、後はリンクをタップして行くだけで裏で履歴を取ってくれるのでかなり時間が短縮出来そうな気がする。

という事で、次回からメール送信やローカルデータベースへの保存の部分を作ってみたい。

ちなみにここまでのプロジェクト全体のソースはこちらからダウンロード可能になっている。(初めてGitHubに上げて見た。^^)
https://github.com/mikehibm/android-browser-intent01

設定ファイルを除いた本体のソース(Intent01Activity.java)は68行しかないので興味がある方はぜひどうぞ。






追記: 標準ブラウザが格納している履歴を取得する方法

こちらに標準ブラウザが保存している履歴をコンテントプロバイダー経由で取得する例を見つけた。機会があればこの方法も試して見たい。
furafura times: 標準ブラウザのコンテントプロバイダから履歴を取得
WebView逆引き - でこちく備忘録



AndroidでURLを開く度に自作のアプリを起動する









.

2011年4月22日

XOOMよりすごいかも! ASUS Eee Pad Transfomerが米国などで発売へ

「ASUS Eee Pad Transfomer」が4月26日から米国などで発売されるとの事。




タブレットとしてのスペック的にはMotorola XOOMとほとんど変わらない感じだが、キーボードと合体出来るという点が面白そうだ。合体するとバッテリーで16時間駆動というのも素晴らしい。

しかもUSBポートに普通のPC用のキーボードやマウスをつないで操作出来るらしい。さらに標準でPCをリモート操作するアプリ(MyCloud=Splashtop Remote)が入っているのも良い。

画面解像度も1280x800あるので、いよいよ開発マシンにリモート接続して仕事がバリバリ出来そうな端末の登場かも知れない。

価格面でも、キーボード付きで比較してもXOOMより安くなりそうだ。

となるとこれはひとまず買ってみるしかなさそうだ。^^



ASUS、「Eee Pad Transformer」を北米で4月26日に発売、価格は$399~ | juggly.cn

Eee Pad Transformer(EPTF)新着動画5 « 恐ろしき寒さののちも桜花

Asus Eee Pad Transformer 発表、キーボード合体で16時間駆動
10.1型1280 x 800 IPS液晶、静電容量式マルチタッチ、1080p動画再生にも対応するデュアルコアのNVIDIA Tegra 2 プロセッサ、512MB / 1GB RAMと16GB / 32GB Flashストレージ、デュアルカメラ、microSDポートにミニHDMIといった基本仕様はスライダーとほぼ同一。OSもおなじAndroid 3.0を採用します。

ASUS「Eee Pad Transformer」のスペック・特徴まとめ(着脱式キーボード付) - NAVER まとめ

ASUS Eee Pad TransformerにSplashtop Remote Desktopが標準搭載~アンドロイド端末からWindowsを操作可能に~(ドリームニュース) - livedoor ニュース
「Splashtop Remoteが搭載された‘Tranformer’は、アンドロイド画面をタップするだけで、WindowsやMacにアクセスでき、一つの端末で複数を楽しむことができる、まさに‘Transformer’です。」とスプラッシュトップ社のCEOである Mark Leeは述べています。
Splashtop Remoteは、アンドロイドマーケットにおいて$4.99で入手できますが、Eee Pad Transformerには標準搭載されており、無料です。



2011/6/24 追記:
より詳しいレビュー記事。
XOOM/Optimus Padとの比較も:タブレットとノートの“いいとこ取り”か?――「Eee Pad Transformer TF101」発売直前レビュー (1/5) - ITmedia +D PC USER
AndroidタブレットはiPadへの対抗意識から、結果として見た目も中身も画一的な仕様になりがちだが、そうした中でEee Pad Transformer TF101は、Android 3.0+Tegra 2による高速な起動やレスポンスと、ノートPCならではの使い勝手のよさがうまくブレンドされ、ほかのタブレットにはない魅力を獲得できた貴重な存在といえる。

日本でも発売されて、さらに詳しい情報が増えて来そうだ。
ASUS Eee Pad Transformer 開封の儀 - Kazzzの日記
「デフォルトではオフになっているがキーボードから一発でスクリーンショットを撮ることが出来る。」 < これはいいな〜。
キーボードつきでノートPCライクに使えるAndroidタブレット「Transformer TF101」ファーストインプレッション - カイ士伝









.

2011年4月16日

Android版Skypeの内部データベースを確認して見た

SkypeのAndroid版にセキュリティ上の問題がある事がニュースになっている。
【注意】Android版skypeで個人情報ダダ漏れ。アンインストールしましょう。 - Hacking My Way ~ itogのhack日記

元々の発見者のブログ記事はこれの様だ。
[Updated] Exclusive: Vulnerability In Skype For Android Is Exposing Your Name, Phone Number, Chat Logs, And A Lot More | Android Police

興味があったので早速自分の携帯でチェックして見た。

とりあえずAstro File Managerで /data ディレクトリを開いて見たが、何も見れなかった。

次に、Android SDKの adb コマンドを試す。
> adb shell
で adb のシェルに入る。
$ ls -l /data
ls -l /data
opendir failed, Permission denied
$
やはり /data ディレクトリの中味は見れない。ただ、/data/data だと一覧が表示された。
$ls -l /data/data
drwxr-x--x app_135 app_135 2011-04-13 16:10 org.openintents.filemanager
drwxr-x--x app_132 app_132 2011-04-13 16:11 com.android.keepass
drwxr-x--x app_130 app_130 2011-04-10 00:41 com.thedeck.android.app
drwxr-x--x app_1 app_1 2010-08-03 20:03 com.htc.weather.agent
drwxr-xr-x app_1 app_1 2010-08-03 19:33 com.htc.CustomizationSetup
drwxr-x--x app_71 app_71 2010-10-05 01:34 com.skype.raider
...
「com.skype.raider」というのが問題のディレクトリらしい。
$ ls -l /data/data/com.skype.raider
ls -l /data/data/com.skype.raider
opendir failed, Permission denied
$
/data/data/com.skype.raider ディレクトリの一覧表示は出来なかった。もちろんこれはRoot化していないAndroid端末では当然の事(のはず)。

次に、/data/data/com.skype.raider/files/shared.xml の存在を確認してみる。
$ ls -l /data/data/com.skype.raider/files/shared.xml
ls -l /data/data/com.skype.raider/files/shared.xml
-rw-rw-rw- app_71 app_71 51210 2011-03-08 15:24 shared.xml
$
おぉ~、確かにあるある! しかもパーミションは「全ユーザーが読み書きOK」になっている。

ではいよいよファイルの中味を表示して見よう。
$ cat /data/data/com.skype.raider/files/shared.xml
cat /data/data/com.skype.raider/files/shared.xml
<?xml version="1.0"?>
<config version="1.0" serial="168" timestamp="1299633891.14">
<lib>
<account>
<default>(Skype ID)</Default>
</Account>
<audio>
<sidipckeyprefix>/tmp/</SidIpcKeyPrefix>
確かに自分のSkype IDが表示された!

さて、さらに内部データベースが格納されているディレクトリを一覧表示。
$ls -l /data/data/com.skype.raider/files/(Skype ID)/

-rw-rw-rw- app_71 app_71 0 2010-10-05 01:35 config.lck
-rw-rw-rw- app_71 app_71 12824 2010-10-05 01:35 keyval.db-journal
drwxrwxrwx app_71 app_71 2010-10-05 01:38 chatsync
-rw-rw-rw- app_71 app_71 40960 2010-10-05 01:35 keyval.db
-rw-rw-rw- app_71 app_71 5776 2011-03-08 15:24 config.xml
-rw-rw-rw- app_71 app_71 12824 2010-10-05 01:35 griffin.db-journal
-rw-rw-rw- app_71 app_71 28672 2010-10-05 01:35 griffin.db
drwxrwxrwx app_71 app_71 2010-10-05 01:35 voicemail
-rw-rw-rw- app_71 app_71 164672 2011-03-08 15:25 main.db-journal
-rw-rw-rw- app_71 app_71 33344 2011-03-08 15:25 bistats.db-journal
-rw-rw-rw- app_71 app_71 487424 2011-03-08 15:25 main.db
-rw-rw-rw- app_71 app_71 69632 2011-03-08 15:25 bistats.db
確かに全部のファイルがパーミッション全開だ。

ではいよいよ main.db というファイルをPC側にダウンロードする。

adbのシェルから抜けてPC側で、
> adb pull /data/data/com.skype.raider/files/(Skype Id)/main.db \work\main.db
を実行すると、一瞬でPC側にmain.dbがコピーされた。

さてこの内容をどうやって確認しようかと思ったが、「SQLite Manager」というFireFoxのアドオンがあったのでこれをインストールして開いてみた。

「SQLite Manager 0.7.0」FireFox Add On
https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/


左側にテーブルの一覧が表示されている。


Accountsテーブルのフィールド一覧はこんな感じ。

(画像をクリックして拡大可能)



EmailアドレスやSkypeクレジットの残高、Call Forward用の電話番号などがあるのが分かる。

公開する為にプロフィールに登録している情報だけでなく、残高やCall Forward用の電話番号など公開するべきでない情報までが平文のままで保存されているのは困る。

Account情報の他に、通話記録、チャット履歴、友人の一覧などのテーブルがある。

これはマズイ。とりあえず今すぐSkypeはアンインストールした方が良さそうだ。
(2011/4/27 追記: この問題は既に修正済みなので最新版が入っていれば今は心配はない)

そうしないと、悪意を持った他のアプリケーションがこれらの情報を自由に利用したりどこかに送信したりする事があり得る。

「悪意を持ったアプリケーションなんてインストールしてないよ」と思い込むのは危険だ。既にインストールされている普通のアプリでも、開発者がその気になればアップデートでSkypeの情報を読み取って送信する機能を付け加える事が可能だ。つまり新規インストールでなく既存アプリのアップデートでもこの脆弱性を利用して情報を盗まれる危険性があるという事だ。



それにしても、どうしてこんな基本的なミスが起こってしまったのだろうか。通常のやり方でDBを作成すれば、自動的に自分のアプリからしか参照出来ない様なパーミッションが付加されるはずだ。
throw Life - AndroidのFile入出力サンプル

ただ、下の方法を使うと他のアプリからでも読み書き可能になるらしい。
OutputStream output = openFileOutput(dst,
Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);

もしこれに似たようなミスが原因だとすると、Android版を開発したプログラマは今頃、職を失っているのではないだろうか。。。いや、テスト担当者の責任になるんだろうか。いずれにせよ、開発者としては身につまされる思いがする。





2011/4/20 追記:
早速Skypeアプリが更新された。更新後、下の通りパーミッションが適切に修正されているのが確認出来た。
$ ls -l /data/data/com.skype.raider/files/shared.xml
ls -l /data/data/com.skype.raider/files/shared.xml
-rw------- app_139 app_139 54794 2011-04-20 12:54 shared.xml
$ cat /data/data/com.skype.raider/files/shared.xml
cat /data/data/com.skype.raider/files/shared.xml
/data/data/com.skype.raider/files/shared.xml: Permission denied
$



2012/4/4 追記:
CodeZineにこんな記事があったのでメモ。
AndroidアプリにおけるDBファイルの正しい使い方:CodeZine
SQLiteDatabase#openOrCreateDatabase を使うとパーミションが644になる。
Context#openOrCreateDatabaseでかつMODE_PRIVATEを指定して使うと660になる。
との事。








.

2011年3月1日

auからHTC Evoが発売へ。でも電池の持ちにはご注意を。

日本でauからHTC Evoが発売されるとの事。
asahi.com(朝日新聞社):KDDI田中社長「もう戻れない感覚」、WiMAX搭載「htc EVO WiMAX ISW11HT」に自信

+525円でWiMAXつなぎ放題、Wi-Fiルータにも:auがスマホのテザリング解禁――WiMAX対応Android端末「htc EVO WiMAX ISW11HT」

WiMAXで快適テザリングできるAndroidスマホ『htc EVO WiMAX ISW11HT』
追加料金がたったの525円で下り最大40MbpsというWiMaxが利用出来るとは、全く羨ましい限りだ。

米国ではWiMaxを使ったかどうかに関わらず(例え対象エリア外であっても)、HTC Evoを持っているというだけでSprintから月10ドルの追加料金を請求される。しかも標準のテザリング機能は4G接続でしか使えず、使う場合は別途月29.99ドルのオプション契約が必要になる。

しかもこの4G(WiMax)というのがそんなに速くない。去年の6月にEvoを買った時は喜び勇んでスピードテストをやったものだが、結果は散々だった。場合によっては3Gよりも遅かったので、ハードウェアの故障を疑ったくらいだった。
ハワイのWiMax
ところが、今日数カ月ぶりに4G接続をオンにして今測ってみたら、この通り8Mbps弱のスピードが出ている。Sprintの通信設備も徐々に良くなって来ているみたいだ。

auの通信事情がどうなのかは分からないが、きっとハワイとは比べ物にならないぐらいに整備されているだろうから、最大40Mbpsという速度も誇大広告ではないのかも知れない。もし本当に20Mbpsとか30Mbpsとかという速度が出るのであれば素晴らしい。

ただ、一つ見落としては行けない点がある。これは4月にauから発売されるEvoも(バッテリー容量が変更されていない限り)多分同じだろう。

それはWiMaxを使った場合の極端なバッテリーの持ちの悪さだ。正確に測った訳ではないが、体感的には3G接続で使用している時の10倍ぐらいの早さでバッテリー残量が減って行く感じだ。実際、今4G接続している10分ほどの間に、バッテリー残量が10%も減った。その間にやった事といえば、メールのチェックと3回ほどのスピードテストだけだ。

つまり、ずっとWiMaxで接続してアクティブにネットを使っていたら、大体100分ほどでバッテリーが無くなる。

購入した時にはデフォルトで4G接続がONになっていて、使ってもいないのに数時間で電池が切れるのでびっくりしてショップに交換しに行こうかと思ったほどだった。

これが理由で、僕は購入以来ずっとEvoでの4G接続を実用的に使った事がない。今回の様にたまに実験的にスピードテストをやってみるだけだ。

Evoでバッテリーを長持ちさせる秘訣は、4Gに限らず3GもWifiもGPSも、使わない機能はとにかくオフにしておいて、必要な時にだけオンにする事だ。僕の場合はこれで50時間は充電せずに使えている。月曜の朝に充電しておけば水曜の朝まで持つという感じなので、これならまあ問題ない。

という事で、auのHTC Evoを購入予定の方はWiMax使用時のバッテリーの持ちの悪さについてはご覚悟を。

しかしテザリング機能が標準なのは、羨ましいなぁ。。。






.

2011年2月5日

Androidのブラウザでリンクをタップした時の枠線と背景色を消す方法

Androidのブラウザでは、リンクをタップするとその瞬間から次のページに移るまでの間、リンク部分に緑色の枠線と薄い緑色の背景が表示される。

機種によってはオレンジ色の場合もあるらしい。




とにかく、この枠線と背景色がとても目障りになる場合がある。JavaScriptを使ったゲームなどを動かしている時は特にそうだ。

そこでこの枠線と背景色が表示されない様にする方法を調べてみた。

Android browser GREEN border on click... - Stack Overflow

-webkit-tap-highlight-color: rgba(0, 0, 0, 0);

というスタイルを該当のリンクに適用すればOKとの事。

実際に試した見たところ、これでバッチリだった。
良かった良かった。









.

2011年1月8日

7インチ超のAndroidタブレットが続々登場

何やら急に7インチ以上のAndroidタブレットがたくさん出て来たみたいで訳が分からなくなりそうだったので、リストにしておこう。
これから買うならやっぱりAndroid3.0搭載のものが良さそう。ちょっと見たところ、仕様的には Eee Pad MeMOとXOOMが一歩抜きん出ている模様。


ASUS 「Eee Pad MeMO」
ASUS、7インチタブレット「Eee Pad MeMO」を発表--携帯電話機能も搭載
Eee Pad MeMO タブレット発表、感圧スタイラス対応&デュアルコアSnapdragon 採用
ASUS EPad: like the EeePad, but with less ecstasy
ASUS Eee Pad MeMo – 7-inch Android Tablet Announced [VIDEO]

MOTOROLA 「XOOM」
MOTOROLA-XOOM 製品情報
Motorola、Android 3.0(Honeycomb)搭載タブレット「Xoom」を発表。2011年第1四半期にリリース
iPadを追撃せよ!モトローラの新タブレットはAndroid3.0マシン

DELL 「Streak 7」
[CES2011]Dell、7インチAndroidタブレット端末「Streak 7」を発表

Samsung 「GALAXY Tab」
Samsung、7インチのAndroidタブレット「GALAXY Tab」発表
Samsung Galaxy Tab 製品情報

AU (Samsung) 「SMT-i9100」
iPadの牙城を崩せるか!?auも7インチAndroidタブレット「SMT-i9100」を発表

NEC 「LifeTouch」
7インチはちょうどいい?NECのAndroidタブレット(LifeTouch)
NECビッグローブが7インチAndroidタブレット『Smartia』を12月6日に発売へ

Camangi 「FM600」
3G通信対応、7インチ液晶のAndroid搭載タブレット端末 (Camangi FM600)
Camangi製品情報


その他にもマイナーなメーカーからもたくさん出ているみたい。
パナソニックも「ビエラタブレット」なるものを開発中とか。

いずれにせよ今年のタブレット市場は楽しくなりそうだ。


---
2/15 追記
これは文章入力には良さそう。ATOKとキーボードでのコピー&ペーストは魅力的。
ATOKも“Ctrl+c”も使えるキーボード付きAndroidポケットノート












.

2010年12月13日

AndroidでiPhoneに匹敵する楽器アプリの作成が困難な理由

Androidでも楽器アプリが出ているけど、試してみるとどれも今ひとつ反応が鈍い、というか画面をタップしてから実際に音が出るまでにタイムラグがあるのがはっきりと感じられる。

iPod Touchだとほとんどタイムラグは感じられないので、この差は何なんだろうと気になった。Androidの仕様上の制限なのか、アプリに原因があるのか。。。

自分でも音楽関係のアプリはいつか作ってみたいリストに入っているので、Androidで任意の音を自由なタイミングで鳴らす方法を調べて見た。Google検索で一応大雑把な所は分かったので、とりあえずメモしておこう。

以下、自分でコーディングして試した結果ではないので「~らしい」「~みたい」ばかりになって恐縮ですが。

Androidで音を鳴らす方法


下の4つの方法があるとの事。それぞれ向き不向きがあるみたいだが、「リアルタイムで任意の音を鳴らす」という用途にはどれもいま一つらしい。(少なくともタイムラグを無くすのは困難みたい。)

1. MediaPlayer

予め作成された音楽を鳴らすのに向いている。
MIDIファイルの演奏も可能。ただしメモリ上に動的に生成されたMIDIデータを演奏させる事は出来ない。一旦一時ファイルに保存する必要がある。

2. JetPlayer

「JetCreator」というツールを使って予めMIDIデータから「JETファイル」という形式に変換しておくと、そのファイルの任意の部分を演奏させる事が出来る。ゲームの効果音などを出すのに向いている様だが、楽器アプリとして自由な演奏をするのにどこまで向いているかは、試して見ないと何とも言えない。
hidecheckの日記 - AndroidでmidiとかJetCreatorとかで鍵盤アプリとか作成
3. SoundPool

MIDIではなくOGGやWAVなどのサウンドファイルを使った効果音を再生出来る。
しずくくんのAndroidでゲームプログラミングしてみたいなblog
むずかしいことはわかりません。

(2011/05/11 追加)
SoundPool使用上の注意点 - Hacking My Way ~ itogのhack日記
4. AudioTrack

PCMデータを直に操作して音を鳴らす場合に使う。(実は良く分からない。^^;)下のソースコードとかを見ると面白そうではあるけど、楽器の音を作るのはかなり大変かも。
にュウさいと - AudioTrackを使う
Androidアプリ開発 / 夢見る少女の開発メモ - 音を作ってみる

(2011/05/11 追加)
たいてい自宅で迷想中 AudioTrackをAudioQueueのように使う

さらに英語でも検索して見た。


英語だと結構核心に踏み込んだ(と思われる)情報があった。
Umito - The state of MIDI support on Android

Stack Overflow - Dynamic Midi generation and playback on Android: Possible?

Google Code - Expose Midi Streaming capability of Sonivox Synthesizer.

Pragmatic Bookshelf - Java SE5 javax.sound (MIDI) classes removed from Android libraries?
やっぱりMIDI音源に対して直接リアルタイムでコマンドを送信する為のAPIが用意されていないというのが致命的なようだ。


現時点での結論(Android 2.2以下の場合)


で結局、今の所はこちらに書いてある通りAndroidの仕様上の理由からiPhoneに匹敵するようなレスポンスの良い楽器アプリを作るのはかなり厳しいようだ。
【combuのDTM & ミキシング 】 脱初心者を目指す音楽制作 - DTMやるならAndroidを買ってはいけない





2/21/2011 追記:
Android2.3, 2.4とか3.0が出てきたので、現在は状況が変わっているかも知れない。
変化がめまぐるしいので最新情報を追いかけるのも大変だ。。。



10/21/2011 追記:
Android 4.0でもまだ改善されていないみたいだ。
Android 4.0で反応の良い楽器アプリは可能になるのか



11/20/2012 追記:
4.2になって少し改善されたみたいだ。

Android 4.2で今度こそ反応の良い楽器アプリは可能になるのか!?


10/25/2013 追記:
最近はこんな感じらしい。
このレイテンシー、Nexus10はDTM用途に使えるかも!? : 藤本健の“DTMステーション” 






 

2010年11月30日

Androidアプリの開発。。。

この本を買おうかどうか思案中。Amazonに誰かレビュー書いてくれないかなー。

初歩からわかるAndroid最新プログラミング

image




実はもう他にも結構Androidの本は買い込んでいるのに、結局サンプルを軽く弄る程度の事しか出来ていない。マーケットに公開出来る様なアプリが作れるのはいつになる事やら。 始めるなら今しかない様な気がして、結構焦ってはいるんですが。会社では今の開発プロジェクトが大忙しだし、家に帰れば帰ったで2人の子育てで手一杯だし、一体どうすればいいのかな。。。^^;