harukazepc’s blog

インターネッツとAndroidなどが大好きです。あとは日々のことなど。

Ti.UI.WebView で、遷移先(遷移しようとしている)URLを知りたい 〜 'beforeload' イベントで url が反映されない件 #titaniumjp #titanium

WebView内での遷移(ページのロードとか、リンククリックとか)のタイミングにあわせて、処理を行いたい場合があるかと思います。

その際に、WebViewでは以下の様なイベントが用意されています。
Appcelerator Developer Center - API for Titanium.UI.WebView

  • beforeload : WebView内でページの読み込みを開始した時
  • load : WebView内でページの読み込みが完了した時
  • error : ページの読み込み中に問題が発生した時

WebViewを利用したアプリケーションの場合によくあるのが、

特定のドメイン(URL)じゃないページへ遷移する場合は、通常のブラウザアプリ(Safari等)で開く

という要件です。


その際、beforeload イベントで遷移先のURLが取得できるように(ドキュメント上は?)なっています。

var webview = Titanium.UI.createWebView({url:'http://www.google.com/'});
webview.addEventListener('beforeload',function(e){
    alert('start moving to'+e.url);
});

ですが、iOSアプリの場合、この e.url が更新されないようです。(※Androidは大丈夫)


このバグ?自体は、下記のように今後修正されるようですが、
https://github.com/appcelerator/titanium_mobile/pull/538
https://github.com/appcelerator/titanium_mobile/commit/63c63147367ce14f242abe0363ce74c729b6c41d#diff-0

これだと iframe 等の読み込みでも発行されてしまうようなので、ものによるとは思いますが、こっちの方が良い気がしたり。*1

--- TiUIWebView.m.orig	2012-03-07 17:18:10.000000000 +0900
+++ TiUIWebView.m	2012-03-09 15:15:03.000000000 +0900
@@ -638,6 +638,16 @@
 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
 {
 	NSURL * newUrl = [request URL];
+
+        if ([self.proxy _hasListeners:@"beforeload"])
+        {
+                if (navigationType != UIWebViewNavigationTypeOther)
+                {
+                        NSDictionary *event = newUrl == nil ? nil : [NSDictionary dictionaryWithObject:[newUrl absoluteString] forKey:@"url"];
+                        [self.proxy fireEvent:@"beforeload" withObject:event];
+                }
+        }
+
 	NSString * scheme = [[newUrl scheme] lowercaseString];
 	if ([scheme hasPrefix:@"http"] || [scheme hasPrefix:@"app"] || [scheme hasPrefix:@"file"] || [scheme hasPrefix:@"ftp"])
 	{
@@ -668,11 +678,6 @@
 
 - (void)webViewDidStartLoad:(UIWebView *)webView
 {
-	if ([self.proxy _hasListeners:@"beforeload"])
-	{
-		NSDictionary *event = url == nil ? nil : [NSDictionary dictionaryWithObject:[url absoluteString] forKey:@"url"];
-		[self.proxy fireEvent:@"beforeload" withObject:event];
-	}
 }
 
 - (void)webViewDidFinishLoad:(UIWebView *)webView
@@ -746,4 +751,4 @@
 
 @end

UIWebViewNavigationTypeとして定義されているもののうち、UIWebViewNavigationTypeOther の場合はbeforeloadを発行しない、としています。

自身のTitanium Mobile SDKの TiUIWebView.m に対して、上記のような修正を加えることで反映されます。
(/Library/Application Support/Titanium/mobilesdk/osx/1.7.5/iphone/Classes/TiUIWebView.m 、とか)

*1:というか、修正が反映されたバージョンだと、'beforeload'の発行が、ユーザによる遷移なのか、それともiframe等なのか、判別できるのかなぁ。。。

Titanium.UI.WebView の UserAgent をカスタマイズする(モジュール作ったよ) #titaniumjp

WebViewのUserAgentを変えたい

※さっさとやり方だけ知りたい方は、飛ばしてね

WebViewにより、サーバ側に用意したサイトなどと連携するアプリを作ったりすることがあると思います。
その際に、アプリから来た時にだけ○○する、みたいなことって、必要になったりします。
その場合、カスタマイズした UserAgent でアクセスし判定することが、一番スマートな感じじゃないでしょうか。

ただ、Titanium には標準ではまだいい感じのUserAgentの変更方法がないようです。(1.8.1現在)

  • Titaniumには、Titanium.userAgent ってのがあるのですが、これはWebViewには反映されません。
  • Titanium.Network.HTTPClient によるアクセスであれば、 setRequestHeader で設定可能。
    • ただし、リクエストを自分で制御して、受け取ったレスポンスをひたすらWebViewに反映していく・・・ってのはきついです。ブラウザ作るか、って感じで・・・

と、そんな感じで個人的にも困っていたので、よっしゃ一念発起(?)、モジュールを作りました!!

TiWebviewUserAgent - customize Ti.UI.WebView's UserAgent

iPhone用とAndroid用のモジュールを作りました。

TiWebviewUserAgent - customize Ti.UI.WebView's UserAgent

user agent を変えるモジュールのアイコン、ってどうしたらいいんだよ・・・

使い方はそれぞれ examples/app.js を参考にしていただければと思いますが、基本的に WebView の描画前に UserAgent を設定する必要がありますのでご注意をば。

githubはこちら。
harukazepc / TiWebViewUserAgent

初めてiPhone/Android両方のネイティブなモジュールを作りましたが、、、うまくIFを合わせられなくてうむむむな感じw
まぁ、元々のUA設定の構造が違うからしょうがないですが。
作り方については、公式のWikiがとてもわかりやすかったです。

必要でしたらぜひぜひご利用くださいませ。
また、問題や要望あればお待ちしております。

Titanium-Google-Analytics がAndroidも対応していた。感謝!

アプリにおいては、DL数は元より、アクティブな利用者数や利用回数、ボタンやメニューの利用頻度など、アプリの品質/媒体力の向上などに解析すべき項目が多くあります。

Titaniumにおけるアプリの利用解析といえば、

のいずれかが挙げられるかと思います。

GoogleAnalyticsを利用すると、おなじみのリッチなUIで、アプリの利用動向などがわかってとても役に立つ!
のですが、Titanium-Google-Analytics では Android に対応していませんでした。

が!

久しぶりに確認すると、Android対応されているじゃないですか!!

Added an user-agent string for the Android platform

おーし、いれかえるぞー!!

「こみれぽ」アプリのこだわりに感動

Android/iOS の両方にアプリを提供しているやつを、ちょろっと探したりしてて。

で、
「どーせ、iOSなUIの焼き直しとか、どっちかのユーザが犠牲になってるのばっかだろーな」とか偉そうなことを思ってたんですが、

「こみれぽ」の事例を見て、びっくりしました。

iOS Android
http://itunes.apple.com/jp/app/id456199245?mt=8 https://market.android.com/details?id=com.navitime.local.crowdreport


見事にそれぞれのOSの作法で最適化されている!!!

超感動。

  • iOSの場合
    • 主に下タブで、機能自体の切り替えにも使う
    • コンテンツ内で、カテゴリの切り替えを行う
  • Androidの場合
    • 主に上タブで、近い機能のカテゴリ切り替え等に使う
    • 機能はアクションバー、もしくはメニューに押し込む

それぞれ設計をし、実装している、ってのは、ほんと手間もかかるし、何より普通の人?はその重要性を気付かないもので。

iOS使いの人な企画者?とかで、「iPhone版と一緒のUIでいいじゃーん」、みたいなのをよく見かけるのですが、もう使いづらい&手抜き感がアリアリとアプリに出るわけで。

動きもきびきび、それぞれのOS上での操作の違和感も無い。

ナビタイムさん、すばらしいです。

エクセルファイルを、読み取り専用で共有して入力は特定の人だけに行う方法

よく、多くの人とデータは共有したいけど、入力は特定の人だけでやりたい、っていうのがありますよね。
そのやり方が自分はわからず、備忘録的に。

  1. 該当のエクセルを開き、「ツール」→「オプション」を開く
  2. 「セキュリティ」タブを開く
  3. 「このブックのファイル共有の設定」で以下を行う
    • 編集用パスワードを設定する
    • 読み取り専用を推奨する
  4. 該当ファイルを保存する

これで次回から開いた時に、

  • パスワード入力欄
  • 「読み取り専用」ボタン

が表示され、編集のためにはパスワード入力、閲覧のみなら読み取り専用で開く、というのが可能になりますね。

設定後に開いた時の上記UIがちょっとわかりにくくて、そこはちょっと残念ですが、こんな感じで。

【Titanium Advent Calendar 2011:十九日目】Androidアプリも作ろうぜ!〜Androidアプリに実装すべきTipsたち

※この記事は、@astronaughtsさん企画の「Titanium Advent Calendar 2011」向けの記事です。

※この記事は、数日前に概要を考えていたのですが、十八日目の@yagiさんの【Titanium Advent Calendar 2011:18日目】Titanium MobileでAndroidと、まさかのネタ被り・・・詳細な内容は違うので、このままいっちゃいますw

前置き(能書き)

僕はAndroidが大好きです!!
Android派(?)でTitaniumな人は、珍しいんじゃないでしょうか。むしろ誰か友達になってください。

僕のような人は、

  • NativeなAndroid派からは、「Androidは裁量大きくてアプリの作りがいがあるなー。Titaniumなにその機能縮小版?」
  • iOSなTitanium派からは、「iOSアプリがjsでサクッと作れれば十分。Androidだっさー。」

と、なんともいえない立場におります。(だいぶ妄想

そんな中、なんでTitaniumやってるかって?
だって、iPhone使ってる人にも、Android使ってる人にも、アプリ使ってもらえるんだよ!最高じゃん!

  • NativeなAndroid派へ。「頑張りすぎずに作るべきアプリもあるよね?iPhone版もさくっといこうぜ。」
  • iOSなTitanium派へ。「ユーザー数が2倍以上に広がるんだぜ!ユーザ属性も違うから、いろいろ広がるぜ!」
    • というかむしろ、アプリによってはユーザ数5倍とかあるよ

Androidな人もどんどんTitaniumに流れてきたら、もっともっとブラッシュアップされるぜ!
TitaniumでiPhoneアプリだけ作ってる人も、もったいないよやろうぜAndroid

ようは使いようなのです。そんな事例や詳細はこちらをどうぞ。
TitaniumでiOS/Android同時リリース:NIFTY-Serveの事例

さぁ、Androidアプリを作ろうぜ!

Androidアプリに必須なTipsたち

そんなわけで、Androidアプリを作る際に必要になる/必須な各機能について、Titaniumでの実装をご紹介。
Android - Documentation & Guides - Appcelerator Wikiの日本語訳+α、といっても過言ではないです。

Intent インテント

Androidの特徴的な機能として、Intentがあります。アプリ間での連携が簡単にできる、という感じ。
暗黙的インテント、という、規定されたアクションだけを指定して連携を開始すると、対応しているアプリケーションの選択になります。

Twitterとの連携は、ユーザがそれぞれ愛用している各種Twitterアプリを使おう、などなど。
Twitter機能をアプリで独自実装しているのはダサイのよ。

こちらが実装例です。

var shareButton = Ti.UI.createButton({title:'share'});

// ボタン押したら、ACTION_SEND対応のアプリへ暗黙インテント発行
shareButton.addEventListener('click',function(e) {
	var shareText = 'share the text';
	var intent = Ti.Android.createIntent({
		action: Ti.Android.ACTION_SEND
	});
	// EXTRA_TEXTとして、shareTextの内容を渡す
	intent.putExtra(Ti.Android.EXTRA_TEXT,shareText);
	Ti.Android.currentActivity.startActivity(intent);
});

WebViewで今見てるサイトのURLをACTION_SENDに対応してるアプリ(Twitterやメール、facebookなど)に渡す、とかをよくしますね。
規定されているアクションは、Androidのマニュアルなどをご参照くださいませ。

逆に、自分のアプリをインテントに対応するようにするには、AndroidManifestへの記述が必要になります。それは後述。

システムリソース(android.R.drawable)の使い方

一般的なOS同様 Androidにおいても、システムリソースが用意されています。OS/端末側で用意した、いわゆるデフォルトで使われる(使うべき)アイコンなどです。

メニューやダイアログ上で使うアイコンが用意されており、これを正しく使う事でグッと質の高いAndroidアプリになりますね。

Titaniumで利用する方法は、下記のような形です。

// オプションメニュー用のヘルプアイコン。
var helpIcon = Ti.UI.createImageView({image:Ti.Android.R.drawable.ic_menu_help});

オプションメニュー

Androidの端末には、ハードウェアキーが複数用意されており、これを使ってアプリも操作することが前提になっています。

Xperia Acroの例↓

「←(バック)」「ホーム」といったボタン以外に、「メニュー」用のボタンがあります。
これは、開いているアプリに対して、設定や情報など、関連する操作を表示するためのボタンです。

こういったメニューが一般的です。設定などもこちらに押し込むのがAndroid流。

Titaniumではこのような実装になります。

// コンテキスト内にて以下を記述
Ti.Android.currentActivity.onCreateOptionsMenu = function(e) {
    var menu = e.menu;
    var menuShare = menu.add({ title:'share app'});
    menuShare.setIcon(Ti.Android.R.drawable.ic_menu_share);
    menuShare.addEventListener('click', function(e) {
        // このオプション選択時の処理
    });
    var menuItem = menu.add({title:'about'});
    menuItem.setIcon(Ti.Android.R.drawable.ic_info_detail);
    menuItem.addEventListener('click', function(e) {
        // このオプション選択時の処理
    });
};

Backボタンなど、ハードウェアキーの制御

メニューキー以外にも、いくつかのハードウェアキーがありますが、それらは Ti.UI.Window にてイベント処理が可能です。

以下は、「←バック」ボタンが押された際の制御の例です。
WebViewでの遷移中にバックキー、当然 WebView内で前に表示したページに戻れるべきですが、Androidでは明示的に制御しないとアプリ(アクティビティ)の終了になります。
そのため、以下のような制御を入れることになります。

var window = Ti.UI.createWindow();
var webview = Ti.UI.createWebView();
window.add(webview);
window.webview = webview;
window.addEventListener('android:back',function(e) {
  var wv = this.webview;
  if (wv.canGoBack()) {
    wv.goBack();
  }
  else {
    this.close();
  }
});
window.open();

アプリ更新に必要な、バージョン設定

Androidには、以下の2つのバージョン設定が存在します。

  • アプリ内部でのバージョン(VersionCode)
  • ユーザに表示するバージョン(VersionName)

重要なのはVersionCodeで、アプリの更新にはこちらが影響します。
前回と比べて大きな整数値であれば、更新する形になります。

マーケット上や端末上のアプリ一覧などで表示されるのは、VersionNameになります。
マクドナルドアプリの例↓

というわけで、アプリを更新する際には、このバージョン設定が必要になるのですが、tiapp.xmlでは、下記のような記述です。

...
    <android ...>
        <manifest android:versionCode="3" android:versionName="1.2">
        </manifest>
    </android>
...

これだと、VersionCodeが2のアプリをインストールしているユーザは、このアプリで更新されます。
そしてユーザには「Version 1.2」と表示されます。

SDカードへのアプリインストール許可

Android端末は、本体のアプリケーション用領域が結構少なかったりします。
なので、iPhone以上にアプリ自体の容量をユーザが気にします。

Android2.2以上の端末の場合、インストールしたアプリケーションをSDカードに移すことができます。
端末設定のアプリケーションから実際に移動できます。AngryBirdsの例↓

ただ、それはアプリ側で許可設定を行わないとできません。
Titanium製アプリはサイズが大きくなりがちで、この対応はユーザに喜ばれますので、ぜひやってください。

tiapp.xmlでの設定になります。

...
 <android ...>
   <tool-api-level>8</tool-api-level>
   <manifest android:installLocation="auto" ...>
      <uses-sdk android:minSdkVersion="4" />
   </manifest>
 </android>
...

minSdkVersionは、アプリの動作対象とするAndroidのOSバージョン。
tool-api-levelは、a2sdのオプションを有効にさせる(構文解析用の)ためのAndroidのOSバージョンです。

詳細?は、こちらの過去記事でどうぞ。

AndroidManifest.xml のカスタマイズ

iOSにおけるInfo.plistが、AndroidにおけるAndroidManifest.xmlです。
Titaniumにおいては、tiapp.xmlをベースに自動生成されるのですが、内容によってはAndroidManifestをカスタマイズする必要があります。

その場合には、以下の手順で。

  1. まず一回buildする
  2. build/android/ 内に生成された、AndroidManifest.xml をコピー
  3. platform/android/ ディレクトリを生成(無ければ
  4. その中にコピーしたAndroidManifest.xmlをペースト
  5. 以降、こちらのAndroidManifestが優先して利用されるので、修正などカスタマイズする。

単純なオプション記述等であれば、tiapp.xml内の内にて記述が可能なのですが、たとえばactivityの設定などは、カスタマイズしないとダメなようですね。

その他詳しくはTitanium公式のドキュメントをどうぞ。

日本語フォントのカスタマイズ

Androidがデフォルトでサポートしているフォントは、とても限りがあります。
電子書籍やAA、アプリの雰囲気を出すためにはフォントを入れることも検討が必要です。

こちらについては、手抜きですいませんが過去記事で!
フォントのカスタマイズ(日本語ttfの反映とか)

ただ、アプリのサイズが大きくなってしまうので、SDカードへの移動許可設定は必須かもですね。

その他

他にも、公式ドキュメントでは、下記の機能などをサンプルとともに紹介しています。ぜひご参照くださいませ。

  • サービス(デーモン、常駐プロセス)
  • ノーティフィケーション(通知)
  • 解像度ごとのリソース(画像など)の配置方法

個人的要望?

Androidな人的には、こういうことがやりたくなります。うまくできないものか。

  • タブのカスタマイズ
    • 現状だとカスタマイズ範囲が狭くてちょっとつらい・・・
    • アクションバー的なviewを入れるのが無理め。
    • 場合によっては、独自でタブの機能を作ったりしてます。モジュール化すべき?
  • アクションバー
    • iPhoneでのNavBar。Android3.0以降ではOS側で実装されていますが、それ以前にもアクションバーの指針があります。
    • これも独自でそういうビューを作って使ってます。これもモジュール化?
  • ウィジェット

おわりに

さて二十日目は、本企画とりまとめの@astronaughtsさんです!!たのしみ!

TitaniumのwikiのAndroid記述が増えとる(というかまとまっとる) #titaniumjp

ひさしぶりに自宅コーディングばかりしてます。
で、これまた久しぶりにTitaniumのwikiを見に行きました。
http://wiki.appcelerator.org/display/guides/

ちょっと前に構成変更やらページ追加やらされたっぽいのは気づいてたんですが、今日たまたま見てたら、やたらAndroid系のTipsが増えててちょっと嬉しくなりました。
http://wiki.appcelerator.org/display/guides/Android

  • Intentとか
  • version管理というか、version設定とか
  • app to SD とか
  • AndroidManifestのカスタマイズとか

Android的には必須なこういったことを、公式Blog上で紹介していることはありましたが、こうやってドキュメントとしてまとめていただくと、リファレンスしやすくいいですねぇ。
(何よりTi界隈のAndroidの存在感がーなので、個人的に嬉しいw)

他にもいろいろ整理/追加されてそうだから、ちょくちょく読んでおきたいですなー。

© harukazepc️