T

Titanium MobileでLocal Notificationを実装する

iOSにはリモートからメッセージなどを通知するPush Notificationとインストール済みアプリから通知するLocal Notificationという二つの機構があります。以前、Push Notificationのサンプルはgistに公開したことがあるので、今回はLocal Notificationについて書いてみます。

Local Notificationが出来るのは

・ポップアップまたはステータスバーへの通知
・通知の際のサウンドの再生
・バッヂの操作(アプリのアイコンの数字のことね)

です。通知が発生した際にステータスバーまたはポップアップのOKをタッチしてアプリケーションを起動することができます。端末がスリープ状態のときはロック解除のところが下のalertActionで指定する文字になります。通知がユーザに無視された、またはユーザがキャンセルボタンをクリックした場合は何も起きません。また設定で通知をオフにした場合は何も表示されません。

*アプリを企画する方はここに注意してください。アプリを通知から必ず起動する方法はありません(やったらリジェクトされます)。

それから、通知を予約したアプリがフォアグランドで実行中には通知は実行されません。ただし、通知が発生していたことはアプリに通知されます。後ほど詳しくみていきます。

サウンドの再生ですが、どうやらシステムに組み込まれたサウンドを鳴らす方法はありません。サウンドデータを自前で用意することになります。対応しているフォーマットはcaf、aiff、mp3まで動作確認できました。wavとかもいけるかもしれませんが試していません。

サウンドの再生は、仕様では30秒までとなっていますが、29秒でないと動作しませんでした。またボリュームはプログラム側から制御することができません。端末でミュートしていたら音は出ません(じゃなきゃ困ります)。

繰り返しは毎日(daily)、毎週(weekly)、毎月(monthly)、毎年(yearly)から選択できます。何も指定しなければ一回だけ実行されます。繰り返しの場合は開始日時に過去の日付を指定すると次回からの実行になりますが、繰り返しがない場合は即時実行になってしまいますのでプログラム作成時に注意しましょう。

以上を踏まえて、作りたいアプリがLocal Notificationを使うのに相応しいかどうかを判断しましょう。

さて、実装です。Local Notificationではアプリいつ、どんなことをするかをiOS側のAPIに予約して、OKボタンの押下などの結果を受け取ります。Resources/app.jsにこんな内容を追記しましょう。

Ti.App.iOS.cancelAllLocalNotifications();
var notifications = [];
notification_params = {
      alertBody: 'こんにちは、こんにちは',
      alertAction: 'OK',
      userInfo: {
        data: {param1:'これはparam1', param2:'これはparam2'}
      },
      sound: 'sound.mp3',
      repeat: 'daily',
      date: new Date((new Date()).getTime() +(1000 * 10))
    };
notifications.push(Ti.App.iOS.scheduleLocalNotification(notification_params));

まずcancelAllLocalNotificationsを実行して過去にこのアプリから登録したLocal Notificationを全てキャンセルにします。必要のないアプリであればここで実行する必要はありませんが、個別に通知を選んでキャンセルすることができないので、こうして一気にやってしまいます。

notificationsという配列を使っていますが、後で利用することはありません。が、アプリの終了後にすぐに消えてしまうと困るのでこうしていちいち宣言しています。

これでdateパラメータで指定された時刻(この場合は10秒後)に通知が実行されます。急いでアプリを閉じて待ちましょう。

実行されましたか?

あ、いまはまだこの記事を読んでいるだけで、実際に手を動かしてはいないんですね。わかりました。でも折角だからやってみましょう。今日は雨だし付き合いますよ。

実行されましたか?

よかったですね。ではもっと細かいところを見てみましょう。Local Notificationの予約はボタンを押せば実行されるようにしますか?でも、ボタンを押した瞬間にアプリを強制的に終了した場合はどうなるんでしょうか。変なタイミングで電話がかかってきてしまったら?Ti.App.iOS.scheduleLocalNotificationはvoid型で戻り値がないので、なんだか落ち着かない感じがしませんか?そうなんです、通常はLocal Notificationの予約はバックグランドで実行するのがベストプラクティスらしいので、ちょっと変更しましょう。値の受け渡しのデモもやります。

app.jsはこんな感じ:

Ti.App.Properties.setString('message', 'こんにちは、こんにちは!');
Ti.App.Properties.setString('when','2012/04/23 00:15:00');
var service = Ti.App.iOS.registerBackgroundService({url:'service.js'});

service.jsをバックグランドで実行するようになっています。Resources以下に設置するservice.jsはこちら:

if(Ti.App.Properties.hasProperty('when')){
	Ti.App.iOS.cancelAllLocalNotifications();
	var notifications = [];
	notification_params = {
	      alertBody: Ti.App.Properties.getString('message'),
	      alertAction: 'OK',
	      userInfo: {
	        alertMessage:'ピンポンパンポン!'
	      },
	      sound: 'sound.mp3',
	      repeat: 'daily',
	      date: new Date(Ti.App.Properties.getString('when'))
	    };
	notifications.push(Ti.App.iOS.scheduleLocalNotification(notification_params));
}
Ti.App.currentService.stop();

アプリがフォアグランドにある場合はLocal Notificationは実行されないといいましたが、実行するはずの時間になるとその旨だけがアプリに通知されます。これはnotificationというイベントとして捕まえることができます。userInfoを使って値の受け渡しまで実行できます。

Ti.App.iOS.addEventListener('notification', function(e) {
  var dialog = Ti.UI.createAlertDialog({
  	title:'お知らせ',
  	message:e.userInfo.alertMessage,
  	buttonNames:['OK', 'お黙り'],
  	cancel:1
  	});
  dialog.show();
});

こんなもんでしょうかね。Local Notificationのパラメータにはいくつか面白いものもあるのでぜひマニュアルをご覧ください。

Titanium MobileでAndroidでカメラ

1.7系になってからAndroidのカメラ機能では問題続きのTitanium Mobileですが、iOSに実装されている、撮影した画像を切り取ってそこだけ保存するCROP機能が欲しかったのでモジュールを作ってみました。

が、公開はもう少し先になります。理由は、これから述べます。

まず、SDK2.3以上では動作したのですが、2.2では動きませんでした。どれくらい動かないかというと、うんともすんともいいません。どうやらonActivityResultがカメラの場合ちゃんと発動しないケースがあるらしく、たぶんマルチスレッドの処理に問題があるんだと思います。そのため、Titanium.Media.showCameraのsuccessでcom.android.camera.action.CROPを呼び出そうとしても、successがちゃんと呼ばれないのでどーしよーもないのです。

そこで、カメラもTitaniumで用意されているものではなく自前でAndroidのAPIを叩いて作ってしまって、そのonActivityResultでああだこうだしようと方針を転換しました。公開が先になるのは、この変更のためです。

もうね、ヘトヘトですよ。同じ苦労を味わっている方、お察しします。

iOS向けアプリでやってほしくない仕様変更

そんなにたくさんアプリを作ってきたわけではないので、これは偏った意見かもしれませんが、iOS向けアプリを作っていて、こんな仕様変更は困るな、というのがいくつかありました。

たとえばアプリ内の特定の機能はIn-App purchaseを利用して購入が完了しないと使えない、という仕様はよくあると思います。この購入フローで、購入手続きが完了したら、前の画面の見栄えが変わっていたりするとかいうのは勘弁してほしいです。全く不可能というわけではもちろんないのですが、不要なレベルで複雑化しがちになってしまいます。


こんな感じ。ほら、購入して戻ると前の画面が勝手に変更されてるでしょ?

最初から計画されていたら構わないんですが、後からこういうのを追加するのが一番困ってしまいます。ウェブから転身した人だと、こんなのJavaScriptで親ウィンドウのノードを書き換えるだけみたいに考えたりしがちなのですが、どちらかというとiOSとかAndroidのアプリだと、画面遷移に「戻る」ボタンを多用すると考えた方がしっくりくるかもしれません。戻るボタンを押して戻った先の画面の内容を書き換えろ、というのはちょっと変だな、って思いませんか?

前にも書きましたが、こういうアプリって、画面をスタックに積んでは降ろすという性質があって、ひたすら前進することを基本にしているウェブとはちょっと違うんですよね。これでうまく伝わるといいんですが。

au、Flash Lite、loadVariables

もうよい子のみんなは知ってるよな!auで、Flash LiteでActionScriptを書くはめになって、loadVariablesを使うときは、いいかい、大事なことだけど一回しかいわないよ!

レスポンスを返すCGIからContent-typeはtext/plainだって教えてあげないと動かないんだよ!

// PHP脳の人はこれをコピペだ!
header("Content-type:text/plain");

OK、わかったよな!わかってないのはauだけだ!ギャーッ

携帯サイトに未来はなかった

jpmobile便利だなあ、でもやっぱり携帯公式サイトに未来は無いわ、と思ったのでメモ。前にも同じことを思ったけど、あのときの思いつきは間違っていなかった。2年以上、携帯公式サイトというビジネスがゆるゆると崩壊する様を見ていて、そう思う。

もし、jpmobileみたいなものを自分で作るとしたら、どんな機能を盛り込んだか。もちろん、絵文字の処理やテンプレートの切り替え、アクセス制限、半角カタカナなどの文字列処理、UIDに関する処理やクッキー食べないdocomo端末を考えたセッションの処理なんてのは、携帯サイトを作ったことのあるある程度以上のプログラマなら当然思いつくだろう。このあたりが全て実装されたjpmobileの機能自体はとてもよく出来ていて、まったくもって不満はない。実際、案件で使ったこともある。

でも、もし本当に携帯公式サイトを楽に構築するためのパッケージを考えるなら、ここにキャリアと連携した月額課金と従量課金の機能を追加しないと完成とはいえない。

もちろん、それがないのはjpmobileの作者が悪いせいでもなんでもない。当然ながら、これくらいの機能を作るだけの能力がないわけがないだろう。そんなことは考えなくてもわかる。でも、もし作ったとしても、公開できないんじゃないかと思うのだ。まず、お金が絡んでくる機能なので、出来ました、はい公開、とやるのは、いくらライセンスなどがあっても、ちょっとリスクが高い。また、それ以上に、課金周りの仕組みを作ってもキャリアのNDAがある以上は、実装でさえもちょっと公開は躊躇われる。

もし、NDAによる制約がなく、エンジニア同士の交流で技術情報の交換が広まれば、jpmobileコミュニティみたいなのが立ち上がって、キャリアが立ち上げる新しいサービスに次々とリファレンス実装を公開していくことで、誰もが簡単に新サービスをサイトに追加していくことができるような世界になれば、携帯公式サイトの立ち上げにはほとんど障壁がなくなり、携帯界隈はアイデアをどんどん実現できる激しい競争の場になる、なんてことも夢想できる。

でも、実際には、公の場でそんなことはできない。携帯サイトというのは、一見するとBtoCのビジネスのようだが、実際にはキャリアに向けてサービスを売りつつ一般ユーザの世話をするようなもので、企業相手のビジネスなのだからエンジニアが所属する企業の枠を飛び越えて何かやる余地は少ない。そこで企業の方が肩代わりしてやろうとするわけだが、企業の倫理から各社が虎の子のように守っているわずかな技術的優位性を手放すわけがない。だから、出来上がるのはビジネス寄りの業界団体が関の山で、いくらコミットしても結局プログラムはキャリアの仕様書とにらめっこしながらゼロから実装するしかなく、だから参加者もこれといって代わり映えしないいつものメンツで、そこでは微笑ましい規模のカルテルみたいなものがほのめかされるだけだ。

携帯公式サイトのサービスというのは、こんなのがあったらいいな、というアイデアが実現される場ではない。なぜなら、そういう思いつきのほとんどが、NDAの壁に塞がれて技術交流が促進されないため、実現するにはコストがかかりすぎるため消えてしまうので、残るのはみんな本当はキャリアが考え出したアイデアで、そのほとんどがくだらない画像をメールに添えて喜ぶようなガキ向けのお遊びか版権ビジネスの延長であり、スマートフォンの登場に至って、遂に競争は救命ボートの場所取り争いの様相を呈している。

おそらく、最後の一手になるであろう、携帯公式サイトの終わりは、少額決済の問題だ。携帯公式サイトのビジネスが最も誇るべきはその課金システムであり、そこを上手に解放できなかったことが終わりの始まりだったわけだが、この一点さえ突破されてしまえば、ビジネスとしての存在意義は携帯公式サイトには存在しなくなる。Appleでは既に実装され、とうとうAndoridでも実施されるアプリ内課金がその一手になり、次は元締めの取り分が大きすぎると怒れる事業者がもっといいソリューションを提案してくるようになると、開発者としてはとても嬉しい限りだ。

というわけで、キャリアのみなさんがAndroidマーケットに小額課金システムを携えて殴り込んできてくれたらいいな、というのが結論でした。

Titanium DeveloperでAndroid(3)

前回の内容からほとんど進捗がなかったので、今回はちょっとしたbkを。

Titaniumではビルドする際に最初からコンパイルし直すかどうかを判定して、時間のかかるコンパイル作業を必要最小限しか実行しないように工夫されている。が、どうやら早すぎる最適化の罠にはまっているらしく、ときどきAPIドキュメント通りに書いたプログラムやサンプルをコピーしただけのテストコードが動かなくなっていることがある。

そんなときは、「Edit」タブから「Titanium SDK」を選択し、現在使用中のSDK以外のバージョンを指定して「Save Changes」をクリックして、もう一度試してみる。その結果は無視して、さらにもう一回「Edit」から「Titanium SDK」のバージョンを戻し、ビルドを実行する。すると、最初からリビルドが始まるので、おかしくなっていた箇所の挙動が修正されることもある。または、プロジェクトのディレクトリ直下にあるtieapp.xmlをtouchでタイムスタンプだけ更新する。

Titanium DeveloperでAndroid(2)

前回まででSDKの用意ができたので、今度はサンプルを動かしてみよう。Titanium mobileには開発元のAppceleratorが公開してくれている「Kithchen Sink」というサンプルがあり、githubから入手できるので、さっそくインポート。失敗したら、新規にプロジェクトを作成してファイルをコピー(とチュートリアルに書いてある)。

ではエミュレータを起動、ほらすごい!といいたいところだが、なんとこのサンプルが、動かないのである。困ったものだ。原因はいろいろあるのだろうが、そもそもエミュレータで起動しない。もうどうしようもないので諦めるしかない。ちなみにiPhoneではちゃんと動く。 update: 動くようになった。

Androidに関してはKitchen Sinkはクソの役にも立たないわけだが、これでTitanium mobileのAndroid向け開発の能力には多いに疑念が湧いたため、シンプルなプロジェクトを新規に作成してサンプルにある各機能を試してみたが、全く先に進まない機能もある。でも試行錯誤するうちに、そんなときには、たまに何かわからないことがあったら、AndroidのSDKからこんなツールを起動してみるといいことがわかった。

Dalvik Debug Monitor

201012242236.jpg  

これは起動中のエミュレータをアタッチしてログ情報を表示してくれる便利ツールで、Android SDKのtools/ddmsという名前で格納されている。ちなみにマルチモニタに対応していないため、MacBookなどで主画面以外で起動するとクラッシュするので要注意。

もちろん、それでもダメなものはダメだ。例えば、ビデオのストリーミング再生はどう頑張っても音声しか出てこない。エラーをみるとバッファの取得中にタイムアウトしているようだが、だからなんだというのか。困ったものだ。

RhodesとiPhone SDK4

Rhodesが出力するbuild.xmlの

iphone:
configuration: Debug
sdk: iphonesimulator3.0

ここを

iphone:
configuration: Debug
sdk: iphonesimulator4.0

にするだけで動作した。

タクシーに携帯を忘れた人クラブ

あちこち出没するタクシーに携帯を忘れた人クラブ」についてまとめておく。

■ 会則 その1 入会資格

(1)肉体的・精神的状態の如何を問わず、乗客として乗車したタクシーに携帯電話を置き忘れた者であること。

(1.1)犬は入れない。

(1.2)くそくらえという場合はその限りではない。

(2)置き忘れた携帯の契約上の所有者である必要は必ずしもない。乗車時に自身が所持している携帯を置き忘れたのであれば入会資格を満たすものとする。

(2.1)自己の所属する組織の所有物であった場合、始末書など何らかの形式で処分されるが、その際はクラブについて言及することは許されない。

(3)忘れた事実に気付くまでの時間の長短は入会資格と一切無関係である。ただしタクシーの乗務員により注意喚起されて気付き、置き忘れた状態が完全に発生する以前であれば入会資格を手にしたことにはならない。

(4)ただし、3の条件において乗務員にお礼をいわない不届き者は「タクシーに携帯を忘れて親切に乗務員に教えてもらったにも関わらずお礼のひとつもいえない反社会的分子」クラブにて再教育される。

■ 会則 その2 退会

(1)原則として退会は認められない。退会する方法も存在しない。

(2)ただし、本当は置き忘れていなかったにも関わらず何らかのアリバイ工作などで置き忘れを偽装していたことが判明した場合、当該会員はあらゆるメディアを通じて会員・非会員を問わずあらゆる方面から悪しざまに罵られた上で強制的に除名されるが、それを期待しての偽装は無駄足に終わるものとする。

■ 会則 その3 活動について

(1)タクシーに携帯を忘れた人クラブの会員は、会則に基づき、置き忘れの理由は全て「ラリー」と名乗る人間によるものであると公言すること。

(2)全ての会員は毎月4で割り切れる日(4日、8日、12日、16日、20日、24日、28日)で、なおかつ年月日に含まれる数字の合計が完全数である(2 + 0 + 1 + 0 + 0 + 5 + 20 = 28)日には、会員の中で成獣となっても最も小さい動物を干支とする人間がいついかなる時に「ラリーのバ~カ」と唱えても必ず「ラリーのバ~カ」と唱和する義務を負う。

(3)ただし辰年の人間にはその資格はない。

SoftBankはちょっと怖い

SoftBankのユーザ向けサービス「My SoftBank」に、ちょっと奇妙な機能がある。PC版のサービスで、ワンタイムパスワードを発行して所定のフォームからログインすると、端末からアクセスしたURLの履歴を期間指定して一覧できる。

ところで、SoftBankといえばドメイン名がないなど独自の書式のURLを特に自社サービスや課金まわりで多用することで知られている。例えばYahoo!ケータイのトップ画面はhttp://ptl/menu/だ。他にも、デジタルコンテンツのダウンロードに固有なURLなどがあるわけだが、その手のものもいろいろと見えてしまう。また、GPSの送信履歴からいろいろと面白い行動履歴を追うこともできる。