[titanium] regionChangedの抑制

Titanium™ Advent Calendar 2013 – Adventar の7日目は、大さん橋を見ながら@mogyaがお送りします。

1466042_10152052877289243_273095704_n.jpg

山下公園でコード書くとかかっこよくない?と思ったんだけど、寒くてコードどころじゃない感じでした…

さてさて、TitaniumにはMapViewというのがあって、これを使うと簡単に地図アプリが作れるのですが、GoogleMapsSDKのバージョンが上がった関係で、現在Androidでは標準のMapViewではなくてModules.Mapを使うように言われていて、ちょっと混乱期にあります。3.2.0で旧バージョンが削除されるらしいので、来年には落ち着くんじゃないかと思うのですけど。現行だと、こんなかんじで書かないといけません。

map_view.js

さらにさらに、MapViewを使って、地図をドラッグした時に発生するregionChangedイベント(これも最近名前がregionchangedイベントに変わりましたw)を契機としてモバイラーズオアシスAPIを呼び出すようにすれば、電源が使えるお店を一覧するアプリを作ることが出来ます。

実際作ってみるとわかるのですが、このつくりだと微妙に使い勝手の悪いアプリになります。ちょっと離れた位置を見ようとして移動を繰り返すと、画面がちょっと動くたびにデータをロードしに行くので、処理がたまってだんだん重くなってしまうのです。

ブラウザ上のGoogleMapsSDKで開発する場合には、処理が落ち着いた時に呼び出してくれるidleイベントというのが存在するので、これを使えば綺麗に実装することが出来ます。

[GoogleMapsAPI] APIと組み合わせる時のイベントはidleを使う – もぎゃろぐ

で、idleイベントが存在しないAndroidやiOSのSDKでどうするか、というのがこの記事のテーマです(前置き長すぎだろうw)

idleイベントと近そうなイベントとして、completeイベントというのがあって、これを使うのも一つのアイデアです。ただこのイベントは、地図のロードが完全に終わってから呼び出されるので、これで実装すると、「ユーザーがドラッグ→地図がロードされる→(一呼吸)→店舗が表示される」という、これまた使い勝手の微妙なアプリになります。スポット数が少なかったり、通信環境が安定していてAPIが高速に応答してくれていれば、余りばれないのですけど・・・

今年リリースした電源検索新バージョンや、iPhone版の電源検索では、regionChangedイベントでそのままAPIを呼び出さずに、キューにイベント情報を積んでおいて、一定時間regionChangedイベントが発生しなくなったら、最後の一回分だけAPIを呼び出す、という実装を行いました。

data_task.js

data_task.js部分が問題のロジックをカプセル化したものです。
regionChangeイベントが発生したらとりあえず全部requestでDataTaskにあずけてしまいます。

mapview.addEventListener(regionchanged,function(evt){
datatask.request(evt);
});

DataTaskは内部でタイマーを持っていて、一定時間が経つと、最後に呼び出したrequestの引数を使って、actionコールバックを呼び出します。

actionコールバックの中でAPI呼び出しを行うようにすれば、おおむね、ユーザーが地図をドラッグしおわったタイミングでAPI呼び出しを行うことが出来ます。

var taskAction = function(self,evt){
Titanium.API.info('taskAction '+evt.longitude+','+evt.latitude);
};
var DataTask = require('data_task');
var datatask = new DataTask({
interval:3000,
action:taskAction,
caller:this
});
datatask.start();

Dalvik_Debug_Monitor.png

書きながら「そこまで頑張る必要があったんだろうか。実はcompleteイベントでいいんだったりして」とちょっと不安になってきたのですけど・・・大丈夫なはずだ。神は細部に宿る!

明日はryugooさん@ChatWorkです。よろしくお願いしますー。

Titanium SDKとiOS/Android SDKの関係

iOS7に対応したTitanium SDK 3.1.3が正式リリースになりました。
さっそく使おうかと思ったのですが、ひとつ気になることが。TitaniumSDKのバージョンをあげるということは、古いiPhoneやAndroid端末のサポートを切り捨てることになったりしないかな?

結論から言うと、3.1.3にあげても市場にある大半の機種で動作するようになっているので、あんまり気にしなくてよかったわけですが、せっかく調べたので一応まとめておきます。

特定のバージョンのTitanium SDKがサポートするiOS/Android SDKの範囲はこの辺に載っています。

まとめるとこんな感じ

Titanium SDK Version Min iOS/SDK Version Max iOS/SDK Version
3.2.0 6.0.x 7.0.x
3.1.3 5.0.x 7.0.x
3.1.1 – 3.1.2 5.0.x 6.1.x
3.1.0 4.3.x 6.1.x
2.1.3 – 3.0.x 4.0.x 6.1.x
2.1.0 – 2.1.2 4.0.x 5.1.x
2.0.x 4.0.x 5.1.x
1.8.x 4.0.x 5.0.x
1.7.1+ 3.1.2 5.0.x
1.7.0 3.1.2 4.3.x
1.6.1+ 3.1.0 4.3
1.6.0 3.1.0 4.2
Titanium
SDK Version
Min
Android/SDK Version
Max
Android/SDK Version
3.1.2
– latest
2.3.3 (API
10)
4.3.x (API
18)
3.1.1 2.3.3 (API 10) 4.2.x (API 17)
3.1.0 2.2 (API 8) 4.2.x (API 17)
2.1.2 – 3.0.2 2.2 (API 8) 4.1.x (API 16)
2.0 – 2.1.1 2.2 (API 8) 4.0.x (API 15)
1.8.x 2.2 (API 8) 3.x.x (API 11)
1.7.x 2.1 (API 7) 3.x.x (API 11)

Android

Dashboards | Android Developers(公式データ)

Version Codename API Distribution
2.2 Froyo 8 2.4%
2.3.3 –

2.3.7
Gingerbread 10 30.7%
3.2 Honeycomb 13 0.1%
4.0.3 –

4.0.4
Ice Cream Sandwich 15 21.7%
4.1.x Jelly Bean 16 36.6%
4.2.x 17 8.5%

2.2は切っていいような気がします。Xperia(SO-01B)とau IS03を切り捨てて、Galaxy S(SC-02B)がぎりぎり生き残るくらいの目安です。
Android 2.3.3以降をサポートすることにすれば、TitaniumSDKは最新の3.1.3でも大丈夫なことになりますね。

iOS

ios_versions.png


iOSとAndroidのバージョン分布、各OS内の割合はiOS6が93%、Android最新バージョン(Jelly Bean)は33%: 「最高のタブレット」を求めて!
(公式データのコピー)

iOS6が93%、iOS5が6%。それ以前は切ってよさそう。iPhone3GSですらiOS6対応ですから、iPhone3Gを切るくらいは十分アリでしょう。
3.1.3(=iOS7対応)でiOS 5.0-7.0サポートなので、これを使って問題なさそうです。

以上、Titanium SDK 3.1.3を使っても古い機種を切る心配はしなくてよさそう、という調査結果でした。
3.1.3、安定して動いてくれるといいのですけど・・・

[TitaniumMobile]いろいろ便利関数集

TITANIUM_logo1_original.png

何となく間に合わない予感がしていたので先送りしていたTitanium mobile Advent Calendar 2012、前倒しで参加表明したら、案の定ネタが思い浮かばないうちに当日になってしまいました。
大丈夫。こういうときは、自作便利関数ライブラリを公開するといいっておじいちゃんが言ってたので、ボクのutil.jsの中身を公開します。


文字列をあれこれする便利関数

前後の空白を削除するとか、改行を落とすとか。

ちなみに、見落としがちですが、printfはGlobal.Stringとしてあらかじめ定義されています。

var_dump

PHPでいうところのvar_dump、オブジェクトの中身が見たいときは、

Ti.API.debug(JSON.stringify(obj));

という具合にすれば見られる。
Ti.API.debug(obj); で見られた牧歌的な時代もあったらしいけど、今試してみたらやっぱりダメだった。

速度の計測

できあがったアプリがなんか遅いのでチューニングが必要だ、となった場合、「推測するな,計測せよ」の原則に従って、まずどこに時間がかかっているのが鉄則です。
ということで、処理時間を計測するためのコード。

こういうふうにして使います。

var trace = new util.TraceLog;
trace.start('hoge handler');
//時間がかかってそうな処理1
trace.lap('hoge handler 1');
//時間がかかってそうな処理2
trace.lap('hoge handler 2');
//時間がかかってそうな処理3
trace.stop('hoge handler');

そうすると、こういうログが得られます。

12-08 02:07:14.035: D/TiAPI(25391): [hoge handler] has started
12-08 02:07:14.039: D/TiAPI(25391): [hoge handler 1] has passed at 2ms
12-08 02:07:14.137: D/TiAPI(25391): [hoge handler 2] has passed at 100ms
12-08 02:07:14.137: D/TiAPI(25391): [hoge handler] has finished at 100ms

処理に時間がかかっているのは「時間がかかってそうな処理2」の部分だ!ということが一目瞭然になります。
さらにtrace.lapをつっこんで犯人を絞り込むもよし、十分にわかったのであれば腕をふるって高速化に着手するもよしですね。

明日は@tady_jpさんですね。よろしくお願いしますー。

svgで画像ファイルを量産する

この記事は、Titanium mobile “early” Advent Calendar 2012の提供でお送りしております。

Titanium Mobileのおかげで、iPhoneとAndroid両にらみでアプリ開発することが現実的になったのはいいのですけど、どちらも機種が増える一方なので、画像ファイルの準備が洒落にならなくなりつつあります。
最低限必須となるアイコンとスプラッシュスクリーンだけで50種類!

Titaniumで用意する画像のサイズ – Sawalog

とはいえ、実際にはほとんど同じ画像がサイズの違うキャンバスに貼り付けてあるだけなので、自動化することにしました。

tiqiita.png

最大で1024pxの画像を57pxまで圧縮することになるので、ビットマップやpng画像をそのまま縮小すると目も当てられない画像になってしまいます。そこで、デザイナさんにはillustratorのベクター画像で納品をお願いすることにしました。
illustratorの「Webおよびデバイス用に保存」機能を使うと、作成していただいた画像をSVGフォーマットで出力することが出来ます。
illustrator.png

SVGは、ベクターグラフィックスなので縮小してもつぶれない、たいていのブラウザでも開くことが出来るなど、期待される要素がたくさんある割にはちっとも日の目を見ないフォーマットですが、それはさておき、プログラマ御用達の画像処理ツールImageMagickやそのRubyバインディングであるRMagickで扱うことが出来ます。

というわけでRubyで書いたコードがこちら。icon.svgとsplash_screen.svgを元に、必要なサイズに拡大縮小、背景は指定した色で塗りつぶした画像を一気に生成します。

こういうのを用意しておけば、「iPhone6が発売されたので新しい解像度の画像が必要」というときでも、プログラマレベルで片付けることが出来るので便利ですよね。

ただこの手法にはちょっと難点があって。ドロップシャドウとか非表示レイヤーなんかはImageMagickがついてこれないのでぐちゃぐちゃになってしまうことがあります。パスで描かれた単純な画像しか適用できないのが辛いので、最近はPhotoShopのアクション機能で出力するほうがいいのかなーとか考えています。そのへんも手法が確立したらそのうちブログで書こうと思います。

[Titanium] webviewのリンク先をブラウザで開く

stockxchng_331498_2788_stormy.jpg

WebViewで開いたページに対して、リンクをクリックした時の挙動をコントロールしたい、というのは人類共通の願いで、過去にもたくさんの方がブログ記事を書かれています。

最初の2つは、webviewのbeforeLoadイベントを使う例で、あとの2つは(もぎゃろぐとひげろぐ。ぷぷぷっ)HTMLの中でリンククリックをハンドリングして、Ti.App.fireEventでアプリに飛ばしたイベントを使って何とかしようという例です。

前者は、iOSとAndroidの違いやTitaniumSDKのバージョンによって動いたり動かなかったりしているのが難点で、後者は、JavaScriptのコードがバグっているとデバッグが超大変(printfデバッグすらできない!)なのが難点です。

さて、ありがちな要求として、ランチャーがわりにローカルのHTMLファイルをwebviewで開いておいて、その後のリンク先は全部ブラウザで開いてほしい、という要望が考えられます。というかボクが欲しかったのにそういうサンプルが見当たらなかったので、自分で書きました。

ポイントとしては

  • beforeLoadは最初のローカルHTMLの読み込みに対しても呼び出されますので、urlが’file://’かどうか確認して、ローカルファイルじゃない時だけ処理するようにしています。
  • beforeLoadでブラウザを開いたとしても、webview自体が次のページを読みに行ってしまうことは阻止できません。できるのかもしれないですけど、うまいやり方を思いつかなかったので、stopLoadingで阻止しつつ、念のため、loadイベントで戻れるときはgoBackさせて強引に最初のページを維持させています。
    これをやっておかないと、特にAndroidでBACKボタンを押してアプリに戻ってきた場合に、次のページを開いているwebviewが見えてしまいます。
  • 余談ですが、Ti.Platform.openURLって、Androidだとちゃんとインテントを聞いてくれるのですね。問答無用で標準ブラウザが開くのだと思っていました。

TitaniumSDK2.1.2GAだと、AndroidとiPhone両方で動作しています。
この例はbeforeLoadイベントを使っているので、将来的にまたSDKがバグったり直ったりすると、また動かなくなるかもしれませんが、その時は自力でがんばってくださいませ:-)

[Titanium]WebViewの遷移先を横取りする

最近まーったくTiを触れていないのですが。書くと役立ちそうな話題を見かけたのでちょっとだけ。

WebViewを利用したアプリケーションの場合によくあるのが、
特定のドメイン(URL)じゃないページへ遷移する場合は、通常のブラウザアプリ(Safari等)で開く
という要件です。
Ti.UI.WebView で、遷移先(遷移しようとしている)URLを知りたい ~ ‘beforeload’ イベントで url が反映されない件 #titaniumjp #titanium – harukazepcの日記

“[Titanium]WebViewの遷移先を横取りする” の続きを読む

【Titanium Advent Calendar 2011:二日目】evalJSの恐怖

【はじめに】

この記事は、@astronaughtsさん企画の「Titanium Advent Calendar 2011」向けに書いています。
@astronaughtsさんは、この企画もそうですけど、それ以外にも関西で最大規模のTitaniumイベントTi.Developers.meetingの主催者でもあります。最初はどうしようかと思うくらいしょぼかったw Ti.Devs.me、諦めずに開催を繰り返して大きなイベントに育て上げた@astronaughtsに、個人的今年のTitaniumMVPを送りたいです。


さて、そんなわけでTitaniumの話題を一つ。Titaniumでは、アプリ内でブラウザが開くwebViewという仕組みを使うことができます。
これを使うと、アプリ内でWEBページを見てもらうことができるので、Titanium以外でもよく使われている仕組みです。これ、意外と怖いということはご存知ですか?

例えば簡単なアプリを用意してみましょう。このアプリは、起動すると、モバイラーズオアシスというサイトをwebviewで開きます。

mo.png
俺ブラウザ — Gist

このアプリの最後に、ちょっとスパイスをかけてみます。

mo1.png

上野駅が蒲田駅に変わってしまいました。これで、モバイラーズオアシスの中の人は、「蒲田駅を検索したら違う位置が表示された」という謎のバグ報告に悩まされることになります。クックック。永久に悩み続けるがいい…

ご自分のアプリのWebViewに上記を仕掛けるとか、絶対やめてください。やめろってば!
冗談はさておき、技術的にこういうことが可能であることは頭においておく必要があります。今回は上野が蒲田になる程度ですみましたが、ニュースサイトの内容を書き換えられたら?ログインしたときにユーザー名とパスワードを他のサイトに送信する仕組みが入っていたら?
webViewとブラウザを同一視していると、あっと驚く手品に巻き込まれてしまうかもしれません・・・

逆に、この手法をうまく使えば、スマートフォンに最適化されていないHTMLに別のCSSを適用して要素を大きく表示させたり、サイドバーを隠してスマートフォン向けのページに見せるようなこともできるかも。
…できるかもしれないのですけど、実際は結構大変です。この記事を書くために色々試したのですけど、evalJSしたJSコードをデバッグする手段が無いので、泣きそうでした。
ちょっと複雑なJSを書くと、なぜか処理が行われなくて途方にくれます。いい思いつきだと思ったんだけどなぁ。

【おわりに】

明日は@rakiさんかな。よろしくおねがいします〜。

[Titanium] iPhoneでAdMaker広告を掲載

Titaniumで作ったiPhoneアプリに広告を出す場合、モジュールが簡単に手に入るAdMobが人気ですが、AdMaker(MediaAd)は使えないの?という声も多いみたいです。やってみたら簡単に出来たので、やり方をまとめてみます。

モジュールを使う

下記を落としてきます。

tryden/TiAdMaker – GitHub

titanium.xcconfigを開くと、TitaniumSDKのバージョンが定義されているので、これを自分の好みのものに書き換えます。1.6.1はちょっと微妙ですね。
titanium.xcconfig.png

で、build.pyを走らせるとモジュールが出来上がります。
build.png
build2.png

出来上がったモジュールを解凍して、新しいプロジェクトにこんな感じで配置。
module-1.png

example/app.jsにサンプルがあるので、まずはこれを動かしてみるのがいいんじゃないかと思います。

app.js-1.png

AD_URL、SITE_ID、ZONE_IDはAdMakerでメディアを作るともらえます。
medibaad-1.png

というわけで起動してみるとこんな感じ。
sample1.png

めでたしめでたし。

webviewを使う方法

 ちなみに、AdMakerの公式サイトでは、こんなふうに案内されています。

TitaniumへSDKを実装できません。どうすればいいですか?
SDKでの対応はしておりません。
Web Viewを作って頂き、そこにJavaScriptタグを実装して頂くことで表示して頂くことも可能です。
(よくあるご質問|スマートフォン広告なら「mediba ad」|iPhone、Androidアプリ・サイト広告)

JavaScriptと空っぽのbodyタグを書いたHTMLを用意してwebviewで開いたところ、広告はちゃんと表示されたのですけど、クリックしたあとの広告もwebviewの中で開いて、どうしてくれようかという感じになりました(当たり前ですね)。
webviewのイベントをハンドリングしてあれこれという手も考えられるのですが、あんまりやると広告に手を入れているように見えてしまうことが懸念されます。モジュールが動くのだったら、そのほうがいいんじゃないかな、と思います。

P.S: Android用はこちら。
titanium の AdMaker モジュール作ってみました(Android用) #titaniumjp – harukazepcの日記

[titanium]TKAnimationSampleはTitanium開発者必見のアニメーションサンプル集

Ti.Developers.meeting vol 0.3 で、MogSnapのコバヤシトールさんに、アニメーションについてお話していただきました。

その際の説明用サンプルとして掲示されたTKAnimationSampleがとても素晴らしかったのでご紹介。

toru0325/TKAnimationSample – GitHub

TKAnimationSample1.png
KitchenSinkみたいに、複数のサンプルが見られるようになっています。

TKAnimationSample2.png
HelloWorld.js。静止画見ても何のことだか分からないと思いますが、HelloWorldが下から飛び出してくるサンプルです。

TKAnimationSample4.png
buttons.js。ボタンが出現するサンプル。よりかっこいいbuttons_with_scale.jsというのもあります。

TKAnimationSample5.png
赤いボールが複雑な軌跡を描いて飛び回るredbox.js。「Hint」ボタンを押すことで、どういうふうに実現されているかを見ることが出来ます。

TKAnimationSample6.png
AndroidやiPadのように画面上にポップアップを出現させるInfo_view.js。iPhoneではあまりやらないことになってますが、実現させたい人はたくさんいると思うので、そういう方はこのサンプルを参考にすると良いと思います。

ゲームやかわいいアプリケーションにアニメーションが必須なのはいうまでもないですが、情報系のアプリケーションでも、ちょっとアニメーションを利かせておくことで、
ユーザーさんの目線を誘導することが出来るのでとても役に立ちます。
APIリファレンスを見て実装してもなかなか上手く動かないのですけど、TKAnimationSampleで動いているサンプルをみて実装するのなら簡単に実装できますよね。
自分はTKAnimationSampleを見て30分くらいで、今作っているアプリにアニメーションを追加することが出来ました。
とても勉強になるので、ぜひさわってみてくださいませ。

[ti.devs.me] window.urlを使わないプログラミング

明日16日のTi.Developers.meeting vol 0.3 in Kyotoで、「window.urlを使わないプログラミング」というテーマでお話させていただきました。

Ti.Developers.meeting.001.jpg
Ti.Developers.meeting.002.jpg
Ti.Developers.meeting.003.jpg
Ti.Developers.meeting.004.jpg
Ti.Developers.meeting.005.jpg
Ti.Developers.meeting.006.jpg
Ti.Developers.meeting.007.jpg
Ti.Developers.meeting.008.jpg
Ti.Developers.meeting.009.jpg
Ti.Developers.meeting.010.jpg
Ti.Developers.meeting.011.jpg
Ti.Developers.meeting.012.jpg
Ti.Developers.meeting.013.jpg
Ti.Developers.meeting.014.jpg
Ti.Developers.meeting.015.jpg
Ti.Developers.meeting.016.jpg
Ti.Developers.meeting.017.jpg
Ti.Developers.meeting.018.jpg
Ti.Developers.meeting.019.jpg
Ti.Developers.meeting.020.jpg
Ti.Developers.meeting.021.jpg
Ti.Developers.meeting.022.jpg
Ti.Developers.meeting.023.jpg
Ti.Developers.meeting.024.jpg
Ti.Developers.meeting.025.jpg
Ti.Developers.meeting.026.jpg
Ti.Developers.meeting.027.jpg
Ti.Developers.meeting.028.jpg
Ti.Developers.meeting.029.jpg
Ti.Developers.meeting.030.jpg
Ti.Developers.meeting.031.jpg
Ti.Developers.meeting.032.jpg
Ti.Developers.meeting.033.jpg
Ti.Developers.meeting.034.jpg
Ti.Developers.meeting.035.jpg
Ti.Developers.meeting.036.jpg
Ti.Developers.meeting.037.jpg
Ti.Developers.meeting.038.jpg
Ti.Developers.meeting.039.jpg
Ti.Developers.meeting.040.jpg
Ti.Developers.meeting.041.jpg
Ti.Developers.meeting.042.jpg
Ti.Developers.meeting.043.jpg
Ti.Developers.meeting.044.jpg
Ti.Developers.meeting.045.jpg
Ti.Developers.meeting.046.jpg
Ti.Developers.meeting.047.jpg
Ti.Developers.meeting.048.jpg
Ti.Developers.meeting.049.jpg
Ti.Developers.meeting.050.jpg
Ti.Developers.meeting.051.jpg
Ti.Developers.meeting.052.jpg
Ti.Developers.meeting.053.jpg
Ti.Developers.meeting.054.jpg
Ti.Developers.meeting.055.jpg
Ti.Developers.meeting.056.jpg
Ti.Developers.meeting.057.jpg
Ti.Developers.meeting.058.jpg
Ti.Developers.meeting.059.jpg
Ti.Developers.meeting.060.jpg
Ti.Developers.meeting.061.jpg
Ti.Developers.meeting.062.jpg
Ti.Developers.meeting.063.jpg