2012/03/05

Android:Intentに格納するURI指定時の注意

アプリ連携時に使用するIntentへ、URI形式でDataを詰めて渡すシーンがよくあります。

例えば、ダイヤラに電話番号を渡す場合は
 Action: android.intent.action.DIAL
 Data  : tel:090xxxxxxxx
とします。

しかし、Dataに指定するURIの生成を誤ると期待しない結果となる場合があります。

下記のコードを試してみます。
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri uri = Uri.parse("tel:090123456789");
intent.setData(uri);
startActivity(intent);

ダイヤラに電話番号090123456789を渡しているコードです。
このIntentを発行すると下記の画面となりダイヤラが起動します。



期待通りの動作です。正常動作しているように見えます。
では、ダイヤラに渡す電話番号を「090123456789#000」に変更した場合はどうでしょうか?
渡す電話番号を変えた下記コードで再び実行してみます。
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri uri = Uri.parse("tel:090123456789#000");
intent.setData(uri);
startActivity(intent);

実行結果は下記です。



結果は前回と同じになってしまい、こちらは期待結果となりませんでした。
URIの#以降が無視されているようです。

問題の原因を探るためにAndroid標準のダイヤラアプリがIntentから電話番号を抽出して
いるコードを拝見してみます。

if ("tel".equals(uri.getScheme())) {
    // Put the requested number into the input area
    String data = uri.getSchemeSpecificPart();
    setFormattedDigits(data);

なにやら、uri.getSchemeSpecificPart()で得られる部分を電話番号として扱っているよ
うです。
ここでのSchemeSpecificPart(以降ssp)とは何でしょうか?
これを知るためにはURIについて少し触れる必要がありますが、URIの詳しい説明はその手
のサイトに譲るとして、今回の問題となる個所だけ抜粋します。
# javaにおけるURIの詳しい説明は http://www.ne.jp/asahi/hishidama/home/tech/java/uri.html
# URIのフォーマットについては http://www.ietf.org/rfc/rfc2396.txt が詳しいです。

今回のケースですと、URIの構造は下記になります。
<scheme>:<scheme-specific-part>#<fragment>

電話番号として指定したdataは
 tel:090123456789#000
URIの構造に当てはめれば、
 scheme = tel
 ssp = 090123456789
 fragment = 000
となります。

ダイヤラアプリが電話番号を抽出していた部分はメソッド名からもssp部分ということが
わかります。
つまりダイヤラはssp部分しか抽出せずfragment部分は無視するので期待結果となりません。

ダイヤラに#以降のfragment部分も適切に渡す方法はあります。
それは、URIのssp部分に指定する文字列をエスケープすることです。
最も簡単な方法はUri.fromPartsメソッドを使用することでしょう。
 Uri.fromParts("tel", "090123456789#000", null);
第二引数はsspとして解釈され必要であれば自動でエスケープしてくれます。

適切にエスケープを行ってダイヤラとの連携を行ったコードは下記です。
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri uri = Uri.fromParts("tel", "090123456789#000", null);
intent.setData(uri);
startActivity(intent);

実行結果は下記


上手く期待通りに動作しました。
この時のURIは tel:090123456789%23000 となっており、#がエスケープされているのがわかります。

「他アプリ連携時に一部の文字が無視される」「Intentに指定したDataが正しく処理され
ない」といった場合は、URIの指定に問題があるかどうかを一度確認するのがよいでしょう。

URIを生成する側は、自分が指定しているのはsspなのか?fragmgnetなのか?
相手先が望むURIの形式は?を意識することが重要です。

# URIについてはssp/fragmentに限らずまだまだ色々な構造があります。
# 今回紹介したのはその一部となります。

# ダイヤラについてのソースコードは(少し古いですが)下記にあります
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/2.3.7_r1/com/android/contacts/TwelveKeyDialer.java?av=f#308

以上です。