猫の手も借りたい

ソフトウエア開発者の多忙な生活

jugglyさんのブログで「いまどこ?」を紹介して頂きました。(その記事はこちら。)知名度のない開発者のアプリなので、このように紹介して頂けたことを大変うれしく思います。

 
調べ物をしていて偶然見つけました。「お手軽位置検索(β版)」5月19日にリリースされていました。今日気付きました。たまたまですが、僕が「いまどこ?」を公開したのはその1日前です。ついでだからその、「お手軽位置検索(β版)」と何が違うか、僕のアプリの宣伝になりますけど書いておきます。

「お手軽位置検索(β版)」は、

  • auの端末向けサービス。Android携帯もau製に限られるみたい。

  • auのAndroid携帯でない端末でも、位置情報の照会だけはできる。

  • 照会要求と結果の通知にcメールを使っているらしい。(なので非Android携帯でも位置情報の照会だけはできるのね。)

  • 海外との間で使うと100円かかったりします。


「いまどこ?」は、

  • OS2.2以降で、位置情報を取得できる全てのAndroid端末(WiFiのみのモデルも含む)が対象。もちろん全世界向け。

  • 照会要求と結果の通知にC2DMを利用。

  • Android端末以外では動きません。

  • 世界中のどの端末の位置情報を照会しても、パケット代以外はかかりません。


です。位置情報を照会したいというニーズは昔からあるので、auが試験的にですがこういうアプリを出してきたのも自然なことだと思います。(どっかで「お手軽位置検索(β版)」と「いまどこ?」の比較レビューしてもらえないかな・・・と期待しちゃいます。)

 
まだかまだかと待っていたのですが、発表のタイミングは見逃しました。いつもチェックしているサイトには出ていませんでした。で、昨日Regza phone T-01CのOS2.2へのアップデートが18日に開始されたことを知り、早速実施しました。WiFiが使えれば本体だけでできるので楽でした。

このアップデートにより、「いまどこ?」がRegza phoneで使えるようになりました。location providerの振る舞いが違うので調査中です。また、このアップデートのために普段使っていないWiFiを利用したのですが、これで、「いまどこ?」の大きなバグが発覚しました。この時点で気付いてラッキーでした。(修正版リリースしました。)

 
Google I/O 2011のセッションビデオにGoogle Analyticsのものがあり、使いたいと思いました。ググると

http://labs.techfirm.co.jp/android/cho/577

で紹介されていましたが、1年半前ですね。結構前からあるのですね。で、こちらも見て「いまどこ?」に組み込みました。アプリがどう使われているかと、位置情報が戻ってくるまでに要した時間の分布を回収します。もちろん個人情報なし、個人を特定できるものなしです。ヘルプにきちんと何をしているか明記しました。

 
2本目のアプリ「いまどこ?」を公開しました。名前の通り、いまどこにいるのかを問い合わせます。位置情報+Googleマップ+C2DMで構成される、Androidならではのアプリです。C2DMの送信割り当て量、App Engine上のコードはちゃんと動いてくれるかなど不安な要素がたくさんありますが、総じて、Androidと共に成長させたいと思っています。

このアプリはカレアラの正式公開後に企画しました。Androidに興味を持ってから見たこちらのビデオに触発され、ずっとC2DMを使ったアプリを作りたいと思っていました。しばらくは問題もいろいろあると思いますが、興味のある方は是非お試し下さい。

 
C2DMを使ったアプリです。特定の相手の端末の位置を照会します。7月末までをβテスト期間にしています。最初はいろいろと問題があると思いますが、興味のある方は是非お試し下さい。

 
Google I/O 2011のAndroid Marketのセッションビデオで、「この先数週間で99カ国を追加します。合計で131カ国で購入できるようになります。」とありました。これはすごいです。これとは別に、ノルウエーのユーザーから、ノルウエークローネの価格が間違っていると指摘があり、訂正しようとして気付いたのですが、いつの間にか販売可能国が増えていました。これからポコポコ追加して131カ国まで増えるということでしょうか。これは実に素晴らしいです。Android Marketの販売可能国が少ないのはずっと問題視されてきていましたので。Googleはこれまでずっと「努力している」とは言っていましたが、なかなか増えませんでした。でも今回の発表できちんと投資をしてくれていたことが分かりました。

カレアラはこれまで配布可能地域を、デベロッパーコンソールで、「すべての国」にしないで、有料版を販売できる国だけを選択するようにしていましたが、もうめんどうなので「すべての国」にチェックを入れました。きっと一部の有料版を販売できない国で無料版を配布してしまうことになり、これは正しくは不適切ですが、実質管理できないので割り切ります。Android Marketのデベロッパーコンソールで、無料版の配布国を、有料版を販売できる国にする、という設定があるといいのですけどね。

 
今日、米国からの郵便が届きました。EIN取りましたよ、というものでした。

Android Marketでアプリを販売する場合は求められないし、iPhoneのAppStoreも現在は不要になったと聞いていますが、Amazon AppStoreやMobiHandはEINを求めます。免税に必要な番号なのですが、このEINの取得ではハマる人も少なくないようです。で、数万円の手数料で申請を代行するという商売もあるようで、ググるとそっちの方がたくさんひっかかります。

さて、僕はAndroid Market以外でもアプリを販売できるようにしたいので、このEINは取得したいものの1つでした。こちらのサイトを参考にして書類に記入して4月17日にFAXしました。

FAXした後で電話もすると良い、という記述も見かけますが、見知らぬ人と英語で電話するのは大変なのでしませんでした。それでも4週間以内に届きました。良かった。

で、同じ悩みを持つ方のために僕が記入したSS-4書類をお見せします。個人情報は塗りつぶしています。

  • PDFに入力し、印刷してFAXしました。左下のSignatureは直筆で。その右側には記入日を APR 17, 2011 みたいに。

  • 4a, 4b は日本の住所を英語表記で書きます。4bの最後は、 Japan, 123-4567 です。最後の番号は正しい郵便番号にします。


EINを取得したい人の参考になれば幸いです。

 
AndroidでGoogle Mapを使うのに必要なAPIキーは、デバッグ版とリリース版(リリース用に署名したもの)で異なりますが、これは開発者にとって共通の頭痛の種のようです。僕も困りました。まず、APIキーを指定する方法は2つあります。

  • レイアウトファイルに、android:apiKey= で記述する。

  • MapViewのコンストラクターで渡す。


MapViewをコードで生成する場合は、デバッグ版かリリース版かに応じて渡すキーを切り替えればいいので簡単です。が、MapViewを使う画面のレイアウトが複雑で、XMLで書いたレイアウトを使いたい場合は前者に目が行きます。僕もそうでした。で、こちらにある方法を試したのですが、どうしても、インフレートしたViewStubをMapViewとして使えません。かと言って、デバッグ版とリリース版で、apiKeyだけ違うXMLファイルを持つのはメンテナンス性が悪いので避けたいです。で、stackoverflowを見ていると、同じ悩みを持っている人への回答として、「MapViewをコードで生成してレイアウトに追加すれば良い」とありました。なるほど。

それで、そのMapVIewを使う画面のレイアウトはXMLで記述しますが、MapViewを入れるところは空のLinearLayoutにし、
    <LinearLayout
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

コードでMapViewを生成してaddViewしました。
        setContentView(R.layout.map);
ViewGroup layout = (ViewGroup)findViewById(R.id.mapview);
String mapKey;
if(BuildOption.isDebugBuild(context))
{
mapKey = mapKeyDebug;
}
else
{
mapKey = mapKeyRelease;
}
mapView = new MapView(context,mapKey);
mapView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
layout.addView(mapView);
mapView.setEnabled(true);
mapView.setClickable(true);
mapView.setBuiltInZoomControls(true);

この方法ではMapViewのコンストラクターにAPIキーを渡すので、.javaのコード中に文字列としてキーを持てば良く、レイアウトファイルに書く必要はありません。僕はこの方が好きです。で、デバッグ版かリリース版かの判定ですが、正しくは、デバッグ用の署名をしているか、リリース用の署名をしているかの判定をしたいところです。こちらには、AndroidManifest.xmlにあるandroid:debuggableの状態を使う例があります。ほとんどの場合はこれでもいいと思いますが、stackoverflowに署名を見て判断する方法があったので、それを使うことにしました。リンク先にある、「Had the same hassle with the API key. Here's a full solution, based on the above link and example from Bijarni (which somehow didn't work for me), I use now this method:」で始まる回答です。ちょいと分かりにくいですが、最初はDEBUG_SIGNATURE_HASHを0などにし、for文の中で sigs[i].hashCode() をログに出して、署名のハッシュを表示させます。署名せずに動かせば、デバッグ版の自動署名の値が手に入ります。それを、DEBUG_SIGNATURE_HASHに入れます。あと、そのコードには、

private static Boolean _isDebugBuild = null;

が抜けていますね。
先日、こちらに端末の回転対応について書きましたが、そこにある方法では不十分なことが分かりました。Galaxy SとXPERIA ARCは逆さの横持ちに対応しています。横持ちは通常端末を反時計回りに回転させますが、その反対でも横向き画面になります。僕が持っているos2.1の端末は、この状態に対応していません。どうやら、os2.2以降だと逆さの縦持ちもアリのようですが、上記端末ではこれはサポートしていないようです。で、Galaxy S(まだ2.2.1を我慢しながら開発に使用中)とXPERIA ARCでは、逆さ横持ちの状態で画面の回転に応答しないようにすると、こちらで触れたコードでは(当然ですが)画面が逆になってしまいます。SCREEN_ORIENTATION_REVERSE_LANDSCAPEにしないといけないからです。で、次のコードにしました。(逆さの横持ち、縦持ちの定義はos2.3以降にしかないので、それより前の端末ではその状態をサポートしているものがあったら、Webによるとあるようですが、下記のコードでは対応できません。Galaxy Sもそうです。)
    public static void setFixedOrientation(Activity activity,Context context,boolean fixed)
{
if(fixed)
{
Configuration config = context.getResources().getConfiguration();
int orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; // warning回避
Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int rotation = display.getRotation();
boolean use_reverse_mode = false;
if(Build.VERSION.SDK_INT >= 9 && (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270))
{
use_reverse_mode = true;
}
if(config.orientation == Configuration.ORIENTATION_LANDSCAPE)
{
if(use_reverse_mode)
{
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
}
else
{
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
}

}
else if(config.orientation == Configuration.ORIENTATION_PORTRAIT)
{
if(use_reverse_mode)
{
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
}
else
{
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
}
}
activity.setRequestedOrientation(orientation);
}
else
{
//this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}

それから、端末の回転対応についてはこちらも必見です。