2010年12月27日月曜日

OS標準のクリッピングを呼び出す方法

Androidで壁紙なんかを設定しようとすると、サイズが大きい場合にクリッピングのインテントへ画像を飛ばしてくれる。
似たようなものを自力で作ってみたんだけど、もとからあるならそれを使わせてもらいたいもんです。

というわけで、「OS標準のクリッピングを呼び出す方法」を調べてみましたが、中々有益な情報に出会わない。
ちゅーか、画像のセレクタからクリッピングへの流れを組むのは

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.putExtra("crop", "true");
startActivityForResult(intent, 0);

てなのが出てくるんだけど。

違うんだよ。 今既にこっちでもっているBMPをクリッピングして返して欲しいんだよ!

ってなると、急激に情報がなくなっていきます。 少なくとも日本語圏ではまともなのがない(探し方が悪いだけって話もある)

なので、実際にクリッピング画面をカメラなり壁紙設定から実行して、DDMSのログを見てどのIntentが呼び出されているのかを調べてみました。

すると、
INFO/ActivityManager(1381): Displayed activity com.android.camera/.CropImage: 863 ms (total 863 ms)

まぁ、863msは置いといて。
com.android.cameraパッケージのCropImageが呼び出されているようです。

ちゅーことは。
Intent intent = new Intent();
intent.setClassName("com.android.camera", "com.android.camera.CropImage");

でウッドボール!!


と、思ったら大間違い。 OS2.1以上だと起動しない!!!

なんと、このクリッピングのインテントはOSによって違う模様。 な・なんだってー!

僕はIS01で開発してたので、エミュレータでOS2.1にしてみると

INFO/ActivityManager(72): Displayed activity com.android.gallery/com.android.camera.CropImage: 1289 ms (total 1472 ms)

うお。 パッケージ名が "com.android.gallery"になってる・・・。

つーか、パッケージが"com.android.gallery"でクラス名が"com.android.camera.CropImage"ってどういことなんだ。

ともかく、
Intent intent = new Intent();
intent.setClassName("com.android.gallery", "com.android.camera.CropImage");

にしてあげたら、OS2.1以上では起動しました。

・・・えぇ、1.6で起動しなくなりましたよ。当たり前ですけど。

なーぬーーーー。

ちゅーわけで、もう一手間。

android.os.Build でSDKのVerがわかるようなので、

final int sdkInt = Integer.parseInt(Build.VERSION.SDK);

で、sdkのVerを取得。 このsdkIntがAPIレベルになるようで。 すると、1.6は4。 2.2なら8です。

それら踏まえて
Intent intent = new Intent();
  final int sdkInt = Integer.parseInt(Build.VERSION.SDK);
  if(sdkInt <= 4){ //OS1.6
   intent.setClassName("com.android.camera", "com.android.camera.CropImage");
  }else{ //OS2.1
   intent.setClassName("com.android.gallery", "com.android.camera.CropImage");
  }  
という感じ。 ふーはー。 とと、本題を忘れていた。 これらインテントにこっちからBMPを渡すんでした。 これもいろいろあるみたいなんですが、Intentで渡せるデータ容量の関係もあって、一度テンポラリとして保存をして、その保存ファイルのUriを渡すものみたいです。 なので僕のソースコードはこんな感じ。
intent.setData(Uri.parse("file://" + url));
  intent.putExtra("crop", "true");
  intent.putExtra("outputX", dw);
  intent.putExtra("outputY", dh);
  intent.putExtra("aspectX", dw);
  intent.putExtra("aspectY", dh);
  intent.putExtra("scale", true);
  intent.putExtra("noFaceDetection", true);
  intent.putExtra("return-data", "true");
  intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + url));
  startActivityForResult(intent, INTENT_CALL_CROP);
urlが渡すBMPを保存したパスだと思いねぇ。 その際に、Intent.EXTRA_STREAMで指定した場所にクリッピングしたデータを書きこんでくれるみたいなので、僕は渡した画像ファイルのパスをそのまま指定してます。 ところで、
intent.putExtra("return-data", "true");
ってー指定があってね。 これによってリザルトが戻ってくるんだかなんだか・・・。 でも、ウェブではどう調べても
intent.putExtra("return-data", true);
って書いてあるんだよね。 本当かね。 ウチの環境ではここでtrueぶち込んでもリザルト返ってこないんだけど。

教えてエライヒト。 それでは、また。ノシシ

2010年12月20日月曜日

ブレークポイントを置いてステップ実行すると読めるのに、通常実行するとBitmapがnullる!

今テストとして、HTTP通信で画像を取ってきて表示する。という至極簡単なものを作っているんですがー。

「ブレークポイントを置いてステップ実行すると読めるのに、通常実行するとBitmapがnullる!」

という現象発生。


調べても同じような事象がみつからず。 しかも常に起こるわけではなくて、開こうとしている画像ファイルのサイズに依存しているような・・・。そうでもないような・・・。

UIスレッド上で動かすからいけないのか? と思って、別スレッド作ってみたものの結果は同じ。

同じどころか、UIスレッド外からImageView操作しようとして怒られたので、Handlerを使ってpostしなきゃいけない始末。

まだInputSreamの準備が出来てないとか、そういうのだろうかー。

AsyncTaskに変えてみようかな・・・。
多分関係ないな・・・。

2010年12月9日木曜日

Bitmap.copyメソッドがPNGの色情報を欠落させる件

突然ですが、アプリをマーケットに登録しました。
Asimoc」という可逆モザイクアプリです。 厳密に言えばモザイクでは無いんですが、パスワードをかけてモザイクをかけれる。と、いうソフトで。

特段難しいことはしてないんですが、「そろそろリリースできそう!」って時に起きた現象が

「Xperiaだと、BitmapをARGB_8888のフォーマット指定してcopyしても、下位ビットの色情報を欠損してくれる」
です。

いや、いろいろ調べていったら、Xperiaが悪いんじゃなくて、AndroidOS2.1ぐらいからのバグ? だか、仕様?だか。

今までは、BMPにピクセル単位で読み込んだり、書き込んだりするために、一回コピーをして、編集可能BMPを取得していたわけです。

Bitmap tempBmp = bitmap.copy(Config.ARGB_8888, true);

 これで、OS1.6までは行けたんだけど、なんかOS2.1からこれだとダメで。

しょうがないので


Bitmap tempBmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),Config.ARGB_8888);
Canvas canvas = new Canvas(tempBmp);
canvas.drawBitmap(bitmap,0,0,null);

と、言う感じに、先に空でBitmapを作って、そこに描画。 と。

なんでこんな事をしなきゃいけなくなったのか・・・。

ううむーん。

2010年12月2日木曜日

シークバーに最小値の概念が無い

今つくっているアプリが大分整ってきたので、設定画面もつくりはじめました。

そんな特殊なことは無いのでpreferenceActivityを継承したアクティビティ作って、xmlで記述したレイアウト(?)を読み込むだけ。

これだけで保存も読み込みも自動でされるんかー。 便利ですなぁ。


しかし、またもや罠。
eclipseでパーツ追加しようと思うとーーー。

CheckBoxPreference
EditTextPreference
ListPreference
RingtonePreference

むむむー。当然あるであろうと思っていた「シークバー」が無い。
するってーと、何かと自作しなきゃなのかー!
って、さらに調べてたら、なんかもう誰かさんがつくっていたっぽい。
http://android.hlidskialf.com/blog/code/android-seekbar-preference

まぁ、素敵。
ということで、使ってみたんだけども。

(ここでやっとタイトル)なんと、シークバーには最小値の概念が無い。びっくり。
maxがあるのにminがないとは!!これいかに!

と、いうわけで、結局、拾ってきたシークバーはそのままでは使えなかったので、ちょっとだけ変更しました。

具体的には、最小値もセット出来るようにして、最大値は max - min としてあげる。
4~32のシークバーが欲しければ、maxは32ではなくて、32-4の28として、シークバー内部の値としては0~28。
でも、画面の表示や、取得時の数値は+minをして4~32に戻す・・・と。

こんな小細工をみんなしてるもんなんでしょうかーー。