先日、Android向け着信音集アプリをリリースしました。
簡単だよなあと思ったのですが、何点か結構かかってしまった点があるので、
メモ代わりに残します。
■後方互換(1.6と2.1への両対応)
基本的に、1.6向けのプログラムは2.1で動くのですが、
一点だけ、アドレス帳の1人1人に着信音を設定出来るのですが、
それが2.1では貼り付けることが出来ませんでした。
(音自体は、リストには入るのですが)
それで、2.1向けに書くと、
Cursor c = mAdapter.getCursor();
int dataIndex = c.getColumnIndexOrThrow(People._ID);
String contactId = c.getString(dataIndex);
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contactId);
ArrayList ops = new ArrayList();
ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(uri);
builder.withValue(ContactsContract.Contacts.CUSTOM_RINGTONE, newRingtoneUri.toString());
ops.add(builder.build());
try{
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch(Exception e) { }
という様に変えることで動作するようになったものの、
2.1向けのapiを使っている部分があり、このままでは1.6では動かないことに。
こういう場合、
http://www.textdrop.net/soft/android-backword-compatibility/
にあるように、1.6設定で、2.1向けの部分のみ、
if(Integer.valueOf(Build.VERSION.SDK) >= 5)
で分離させ、reflectionを駆使して
以下のように書き換える必要がありました。
ArrayList ops = new ArrayList();
Class contentProviderOperation = Class.forName(“android.content.ContentProviderOperation”);
Method newUpdate = contentProviderOperation.getMethod(“newUpdate”, new Class[] { Uri.class } );
Class contentProviderOperation_Builder = Class.forName(“android.content.ContentProviderOperation$Builder”);
Method build = contentProviderOperation_Builder.getMethod(“build”, null );
Method withValue = contentProviderOperation_Builder.getMethod(“withValue”, new Class[] { String.class , Object.class } );
Class ContactsContract_Contacts = Class.forName(“android.provider.ContactsContract$Contacts”);
Uri CONTENT_URI = (Uri)ContactsContract_Contacts.getField(“CONTENT_URI”).get(ContactsContract_Contacts);
Uri uri = Uri.withAppendedPath(CONTENT_URI, contactId);
String customRingTone = (String)ContactsContract_Contacts.getField(“CUSTOM_RINGTONE”).get(ContactsContract_Contacts);
Object builderObject = ((Object) newUpdate.invoke(null, uri));
withValue.invoke(builderObject, customRingTone, newUri.toString());
ops.add(build.invoke(builderObject, null));
Class ContactsContract = Class.forName(“android.provider.ContactsContract”);
String AUTHORITY = (String)ContactsContract.getField(“AUTHORITY”).get(ContactsContract);
Method applyBatch = ContentResolver.class.getMethod(“applyBatch”, new Class[] { String.class , ArrayList.class});
applyBatch.invoke(mainContext.getContentResolver(), AUTHORITY, ops);
全くreflectionなど使ったこと無かったので、
java的に、書いていくのが大変でした。
■HT-03Aで・・
着信音を登録していくと、どんどん着信音リストに貯まって行ってしまうので、
端末にある着信音のリストを見て、もし既にあればそのUriから設定するようにしたところ、
HT-03Aでは「アプリが停止しています 強制終了しますか、待機しますか」
というような”待ち”ウインドウが出てしまいました。
(他の端末や、エミュレーションでは問題なし。)
ちなみにリストからのチェックは、以下のような関数を実行していて、
音は30個とか40個とかしかなくとも、もうその数だけでも”待ち”状態になってしまいました。
public static Uri ringtoneExsistUri(Context mainContext , String soundName , int RingToneType){
RingtoneManager rm = new RingtoneManager(mainContext);
rm.setType(RingToneType);
Cursor cursor = rm.getCursor();
if (cursor != null) {
int repeatCount = cursor.getCount();
for(int i = 0 ; i < repeatCount ; i++){
Ringtone ringtone = rm.getRingtone(i);
String currentTitle = ringtone.getTitle(mainContext);
if(currentTitle.equals(soundName)){
return rm.getRingtoneUri(i);
}
}
}
return null;
}
登録までには、
上記の既にあるかチェック→リソースから音ファイルに変換(ファイル化してどこかに置かないと着信音として設定出来ないので)→設定
という流れがあるのですが、それらを全てバックグラウンド動作に書き換えることで、
この現象は回避出来ました。
(バックグラウンド動作は、
http://wikiwiki.jp/android/?AsyncTask%A4%C7%A5%D0%A5%C3%A5%AF%A5%B0%A5%E9%A5%A6%A5%F3%A5%C9%BD%E8%CD%FD%A4%F2%B9%D4%A4%A6
を参考にさせていただきました。)
やはり「1本ちゃんと出す」と言うことは
結構いろいろありますが、すごく勉強になりました。
ちなみに、アプリは「着信音~日本の音」
http://www.zerohachi.jp/ringtone01/index_jp.html
という、そのままの名前で99円でマーケットで販売しております、
もしよければご購入いただければありがたいです。
※その他、参考にさせていただいたサイト
http://d.hatena.ne.jp/tomorrowkey/20090826/1251294895
http://code.google.com/p/ringdroid/
http://clipboard.blog.so-net.ne.jp/2010-05-01
kuni android, プログラム
public void onTouchEvent(MotionEvent event)
でタッチを拾うと、Homeのメニューを開くボタンや、
そのメニューのアイコンをクリックしたときなどにもタッチを拾ってしまう。
そこで、
http://developer.android.com/intl/ja/resources/articles/live-wallpapers.html
にも載っているが、onCommandを書き、
setTouchEventsEnabled(false);
を入れonTouchEventへ渡らないようにすることで、
壁紙へのタッチのみを拾うことが出来るようになる。
@Override
public Bundle onCommand(String action, int x, int y, int z,
Bundle extras, boolean resultRequested) {
if (“android.wallpaper.tap”.equals(action)) {
//action
}
return super.onCommand(action, x, y, z, extras, resultRequested);
}
ただ、なぜかエミュレーターでは動作せず、実機(Desire)では動作するという、
変な状況になってしまっている。
kuni android, プログラム
NSUnknownKeyExceptionというエラーが出てアプリが落ちることがあり、調べてみると、
InterfaceBuilderから、既に削除してしまっていたclassを参照してしまっていたため、起こっていました。
また、この様な状態だと、アウトレット接続が残ってしまう
(controlクリックで接続状態を見ると、黄色の文字で警告っぽくなっている)
事があるので、これもちゃんと外さないと、エラーが出続けてしまってました。
kuni ソフトウェア
GAE/JAVAで、なんてことは無いエラーだったのですが、
JSPファイルを追加しただけでエラー(JSPに赤い×マークが付いてコンパイルできない)になるのが
何でなのかわからず、しばらく調べてしまいました。
http://groups.google.com/group/google-appengine/browse_thread/thread/6f445b256a5aec0f
によると、JDKが入っているフォルダを選択しないとだめで
(自分の環境だと、eclipseの、window→preferences→JAVA→Installed JREsから、
C:\program files\Java\jdk1.6.0_18 のフォルダを選択して追加し、
それを選択することでエラーは出なくなりました。)、
JREにはコンパイラが付いていないからだそうです。
kuni プログラム
mixiアプリモバイル製作で、
自分が詰まっていた項目を書いていこうと思います。
しかし、ソース全部などはmixiのサンプルも載せないといけなくなり、
それだと規約違反になってしまうと思うので、
mixiのサンプルにどう手を加えていったのかという観点で書いていきます。
まずはかなり基本的な事から・・
(すみません、WEBアプリ超初心者なので、
レベルはかなり低いところから始まってると思います)
●1
mixi developerサイトのサンプルを見て進めていくと、
import oauth 部分でエラーが出てしまう。
これは自前で用意する事になるのですが、自分は
http://code.google.com/p/oauth/
を使わせてもらいました。
(oauth.pyをindex.yamlなどと動階層の、一番上の階層に入れておけばOK。)
●2
携帯向けのページはshift-jisなので
mixiのサーバから取得した情報もshift-jisに変換する必要がある。
(当たり前ですね・・。)
サンプルに用意されている「getPerson」関数の流れの後だと、
person = get_person(owner_id)
entry = person['entry']
displayName = entry['displayName']
displayName = displayName.encode(‘shift-jis’)
といった感じで、一度変換する必要がある。
●3
mixi developerのサイトのPersistence APIのサンプル
create_appdata関数の上の、
params = {‘xoauth_requestor_id’: ’000000′}
の値は、当然ながら ’000000′の部分は自前で入れる必要がある。
(これに気づかなく、なんでcreate_appdata関数実行しただけで
エラーになるんだろう・・と思っていた。)
ページ遷移とAPIアクセス に載っている、
get_person(user_id)関数と同じように、
owner_idを受け取り、その関数の中でparamsを設定するようにすれば
いいかと思う。
(むしろサンプルもそうなっていて欲しかった・・みたいな)
●4
mixi developerのサイトのPersistence APIのサンプル
read_appdata関数などで良く出てくる、
urllib2.urlopen(r) は何を返すのか、と言うことが解っていなく、
そのまま出力すると「<」とかが出てしまっていたのですが、
http://d.hatena.ne.jp/yumimue/20071231/1199129495
を見させていただくと、文字列のみで返ってくる訳ではないので、
ちゃんと整形する必要がある。(これも当たり前ですね・・。)
read_appdata関数に付け足すのであれば、
最後のreturnの部分を、
return simplejson.loads(urllib2.urlopen(r).read())
のようにsimplejsonを使い、取りやすく返すようにして、
readData = read_appdata(‘point’ , owner_id)//3の部分同様、owner_idを送るように改造している//
readData = readData['entry']
readData = readData['mixi.jp:' + owner_id]
readData = readData['point']
でサンプルで保存していた「point」の値が取り出せる。
●5
postで送信した際、受け取り側で文字化けする。
これは、
http://d.hatena.ne.jp/gonsuzuki/20090129/1233298532
のサイトを参考にさせていただいたのですが、
「self.request.get()を使うまえに、self.request.charsetを定義します」
との書いてある通り、データを、
「self.request.get(“postData”)」といった形で受け取る前に、
self.request.charset = “Shift_JIS”
を入れて、明確にshift-jisデータだと指定しないといけない。
そうでないと、文字化けどころか、1文字も受け取れなかったりしました。
※PC版(JavaScript+OSDEで作れるのでテストが楽)の方が、
かなりどころか自分でも1週間くらいでそれなりの物が出来たくらい、
作りやすいです。
モバイルでは、PHPだとデバッグ環境も構築出来るみたいなんですが、
GAE+Pythonでmixiモバイルだと・・無いようなので、
いちいち携帯に転送してテストしてます。かなり・・大変です。
kuni mixiアプリ モバイル
iPhoneに、バーナムラボラトリー様制作のアニメが丸々1本入ったアプリ、
「星に願いを Fantastic Cat」をリリースしました。
http://itunes.apple.com/jp/app/id346781455?mt=8
セール期間につき、230円で販売中です。
星に願いをに関しては、
http://gigazine.net/index.php?/news/comments/20090212_cold_body_warm_heart/
http://gigazine.net/index.php?/news/comments/20090803_fantastic_cat/
にも記事になっております。
kuni ソフトウェア
こんな問題あったっけ?という問題にはまってしまいました。
classのコンストラクタで、
gotoAndStop(1);
を実行してはいけないのですね。
1フレームで止めるように使うことすら、だめなんですね。
そういう時はStopにしろと。
(でも、初期化の関数を再利用したい場合などで、
そういう風に書くこともありえますよね。
自分がそうだったからなんですが・・。)
これを行ってしまうと、一見エラーなどは当然出ず、正常に動くんですが、
それを設定しているオブジェクト(詳細は未確認ですが、設定しているオブジェクトを内包しているmcでもだめかも)
への参照が、どうやっても取得できず、nullになってしまいます。
それで、
「TypeError: Error #1009: null のオブジェクト参照のプロパティまたはメソッドにアクセスすることはできません。」
がいろんな所で出て、かなり焦りました・・。
でも、自分でそのクラスをnewして、addChildして使う分には正常に動くという・・。
FLASHの仕様でしょうか・・。
kuni プログラム
チャプター情報付きの動画の話なのですが、
mac上のQuickTimeで見たり、iTunesから同期してムービーファイルとして扱うときには
チャプターを操作出来るのですが、アプリに入れた際にはそのボタンが出なくなってしまっていて、
調べたのですが、やはり・・出来ないようです。
http://stackoverflow.com/questions/96156/chapters-in-videos-for-the-iphone
ただ、OS3.0からinitialPlaybackTimeというプロパティができ、
ムービーの開始時間を指定できるようになったので、
メニューを作って、そこから再生と言うことをやれば、
DVDのメニューなどと少しは似たような事が出来そうです。
(開始時間が変わるだけで、再生してしまえば、後は通常の再生と変わりなく、
開始に指定したよりも前の時間に戻ったりと言ったことも問題ない)
kuni プログラム
2Dの画像の上に3Dオブジェクトを合成できたと言うことは、
カメラの映像とも合成できるのかな?と思い、やってみることにしました。
カメラの動画部分は自由に取れないので(非公開apiを使えば出来る?)、
撮影前は3Dモデルを上に載せ、撮影後に
didFinishPickingMediaWithInfoでイベントを拾い、画像合成を行うことにしました。
(なので実用アプリとしては少し矛盾も出てしまっています・・。
撮影後、Preview画面がどうしても出てしまい、その画面に移行したことが
SDKからは取れないので、そこでも3Dモデルが動かせてしまう・・とか)
OpenGLESのViewからUIImageへは、
■iPhone – saving OpenGL ES content to the Photo Album
http://www.bit-101.com/blog/?p=1861&paged=2
をそのまま使わせていただきました。
(※上記blogのコメントの通り、
27行目、CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
の部分を
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
とすると背景が抜け、上手く背景と合成することが出来ました。)
動作画面
撮影後の写真
自分は上手くいかなかったのですが、
カメラのアクションをぶんどる方法
http://devlog.feedtailor.jp/items/detail/5/iPhone
等があるようなので、こういうのを入れていけば、もっと完成度は上げられるかもしれません・・。
kuni プログラム openGL
チュートリアル3でテクスチャ付き
Cubeの読み込みが出来ました。
■ブレンダーからiPhoneへ(SIO2エクスポータの実験)
http://pastel.slmame.com/e656887.html
も非常に参考にさせていただいたのですが、
これによると、単純にモデルを変換した「.sio2」ファイルを
自動的に読み込む訳ではなくて、
sio2側(xcode側)からどの部分を読み込むのか
指定が必要っぽい事が分かりました。
(※チュートリアル2と比べても、
sio2ResourceBindAllImages( sio2->_SIO2resource );
sio2ResourceBindAllMaterials( sio2->_SIO2resource );
と言った命令が「templateLoading」
部分に増えている。
しかし、特に変わっているのはそこだけのようなので、
以外に簡単にいろいろ読み込めるようになる?とも思いました。)
引き続きチュートリアル4へと行き、
もっとsio2を見ていきたいのですが・・
ひとまず先に、xcode上で2Dの画像と合成する
と言うのをやってしまおうと思います。
(Blender上でPlaneを作り、そこに画像を貼った方が
早そうなんですが、今回の場合、仕様的にxcode上で
操作したいんですよね・・。)
どういう風にやるのかどこかに載っていないかなあと思いきや、
■iPhone SDK OpenGLのビューの背景を透明にする方法
http://d.hatena.ne.jp/uosoft/20090807/1249570897
という風にまさにやりたいことが書いてあるサイトがあり、
この手順通りにし、合成を行うことが出来ました。
(ビデオチュートリアル2と合成)
おお、これで取りあえずの仕様は満たせたのかな?
しかしモデルやカメラを動かしたりしたいと言ったことは
必ず出てくると思うのと、
まだ自由にBlenderモデルを読み込めていないので、
引き続きsio2のビデオチュートリアルを見ていこうと思います。
kuni プログラム openGL