GalaxyNexusをUbuntu12.04に接続する

残念ながら日本では生産が終わってしまったGalaxy Nexus。私は予約までして購入したんですが、やっぱり日本では売れなかったみたいですね。


閑話休題

このGalaxy Nexusですが、ご存知の通りPCにUSBケーブルで接続するとUSBマスストレージデバイス (たとえばUSBメモリ) ではなく、MTPデバイスとして認識されます。Windowsでは標準でMTPに対応しているため (そりゃそうか) 何の苦労もせず、ぶっ挿せばUSBマスストレージデバイスかのように扱うことが出来ます。MacだとGoogleが専用のアプリケーションを公開しています*1
でもLinux用のツールは残念ながらありません。ていうかあるんですがまともに動作しません*2。Galaxy Nexusにもがんがん音楽ぶっこみたい、と思うのが人の常。ということでその方法をいろいろ試してみました。

Rhythmbox

Galaxy NexusをUSBケーブルでPCに接続すると、どのアプリケーションを起動するか聞いてくるんですが、その中の一つにRhythmboxがあります。このアプリ、Ubuntu12.04の標準音楽プレイヤーなんですが、Galaxy Nexusとの相性はいまいちです。。。というか最悪です。PCに保存してある音楽ファイル (ogg) を同期しようとするとほぼ100%シャットダウンします。ほぼというのはごくたま〜に成功することがあったからです。
なので却下。

Amarok

Amarokもとても有名な音楽プレイヤーアプリです。でもこちらもGalaxy Nexusとの相性は最悪でした。落ちたりすることしばしばなのですが、何よりも遅い!音楽ファイルの同期に信じられないくらい時間がかかります。そもそも起動もめちゃめちゃ重いし。
なのでこちらも却下。

gMTP

Windows Media Playerのようにエレガントに音楽ファイルを同期することは諦めて、MTPデバイスをUSBマスストレージデバイスのように考えて手動で管理することを考えました。で見つけたのがこちらのアプリ。参考にしたサイトはこちらとかいろいろ。

http://www.gaggl.com/2012/05/accessing-samsung-galaxy-nexus-as-usb-media-device-ubuntu-12-04/


残念ながらこちらも最悪の相性。。。とにかくよくエラーが発生する。エラーが発生しなくてもとてつもなく遅い。フォルダ一つ作るのに5〜6分かかってしまう。
残念ながら却下。

fuse直叩き

最後にたどり着いたのがこちら。結論からいうとなかなかイイ感じ。参考にしたサイトはこちら。

http://www.omgubuntu.co.uk/2011/12/how-to-connect-your-android-ice-cream-sandwich-phone-to-ubuntu-for-file-access/


今のところほぼ問題なく動作してる。速度も申し分なし。上記サイトは英語なので、簡単な説明を記載しておきます。


まずは必要なパッケージのインストール。

% sudo apt-get install mtp-tools mtpfs


続いてGalaxy NexusをUSBケーブルで接続して以下のコマンドを実行。

% mtp-detect | grep idVendor
% mtp-detect | grep idProduct


するとこんな感じで反応する。いずれも10秒ぐらいかかります。

% mtp-detect | grep idVendor
Device 0 (VID=04e8 and PID=685c) is a Samsung Galaxy Nexus/Galaxy S i9000/i9250, Android 4.0 updates.
   idVendor: 04e8
% mtp-detect | grep idProduct
Device 0 (VID=04e8 and PID=685c) is a Samsung Galaxy Nexus/Galaxy S i9000/i9250, Android 4.0 updates.
   idProduct: 685c


idVendorとidProductの値を覚えておきます。
次に/etc/udev/rules.d/51-android.rulesというファイルを作成します。

% sudo vi /etc/udev/rules.d/51-android.rules


以下の内容を入力して、保存。

SUBSYSTEM=="usb", ATTR{idVendor}=="VENDORID", ATTR{idProduct}=="PRODUCTID", MODE="0666"


VENDORIDとPRODUCTIDを先ほど表示したidVendorとidProductに書き換えます。上の例であればこんな感じ。

SUBSYSTEM=="usb",ATTR{idVendor}="04e8",ATTR{idProduct}="685c",MODE="0666"


次にGalaxy NexusをPCから抜いて以下のコマンドを実行します。

sudo service udev restart
sudo mkdir /media/GalaxyNexus
sudo chmod a+rwx /media/GalaxyNexus
sudo adduser YOURUSERNAME fuse


YOURUSERNAMEにはもちろんユーザ名で置き換えます。
次に/etc/fuse.confというファイルを編集します。

# Set the maximum number of FUSE mounts allowed to non-root users.
# The default is 1000.
#
#mount_max = 1000

# Allow non-root users to specify the 'allow_other' or 'allow_root'
# mount options.
#
# user_allow_other


最後の行のコメント (#) を外して保存。そしたら再起動します。
再起動したら、以下のエイリアス名をシェルの設定ファイルに保存します。

alias android-connect="mtpfs -o allow_other /media/GalaxyNexus"
alias android-disconnect="fusermount -u /media/GalaxyNexus" 


これでようやく設定はおしまい。Galaxy NexusをUSBケーブルでPCに接続します。そしたら以下のコマンドを実行。

% android-connect


Nautilusを立ち上げてしばらくすると/media/GalaxyNexusが出てきます。あとは通常のファイルシステムと同じように操作することが出来ます。


一週間ぐらい悩んでたんですがようやく解決したかな。
めでたしめでたし。

*1:Android File Transfer

*2:もしかしたら私だけ?

App Engineのテンプレートファイルと静的ファイルの場所

最近Google App Engine for Goを始めました。
なんとなく興味本位で触ってみたんですが、思いの外Goがいい感じだったので、これからApp Engineの勉強はGoを使おうと思いました。なんと言うかとっても自分にフィットするんですね。


閑話休題
今日のお題はApp Engineでよく使われるHTMLテンプレートファイルとcssや画像などの静的ファイルの場所について。この一件で私は三日棒に振りました (プライベートの時間ですよ) 。


App EngineのTutorialやSDKに付属しているサンプルをみるとだいたいこんな感じの構成になっています。

hogehoge ---+--- app.yaml
            |
            +--- hogehoge --- hogehoge.go
            |
            +--- hogehoge.html
            |
            +--- style.css


つまり、HTMLテンプレートファイル (上の例ではhogehoge.html) がapp.yamlと同じフォルダに配置されている、ということです。でもこんな構成にしたくなりませんか?

hogehoge ---+--- app.yaml
            |
            +--- hogehoge --- hogehoge.go
            |
            +--- templates ---+--- hogehoge.html
                              |
                              +--- style.css


なぜかというと、HTMLテンプレートファイルの数が多くなってくるとトップレベルにファイルがゴロゴロ置かれることになってしまい、ごちゃごちゃした構成になってしまうからです。


フォルダを作ってファイルを移動するのはそりゃ出来ます。では移動したHTMLテンプレートファイルを使うにはどうやったらいいのでしょうか?まずはトップレベルにHTMLテンプレートファイルが存在する場合。

t, err := template.ParseFiles("hogehoge.html")
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

err = t.Execute(w, nil)


なんとなく分かりますよね。hogehoge.htmlを読みこんで (ParseFiles) レンダリングする (Execute) 。
ではHTMLテンプレートファイルの場所を変えた場合は?

t, err := template.ParseFiles("templates/hogehoge.html")
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

err = t.Execute(w, nil)


と思うでしょ?
このコードを開発用のローカルサーバで動かすと何の問題もないのですが、App Engineにディプロイすると、


あれれ?そんなファイルないと怒られてしまいました。


…それから三日後。原因はapp.yamlにあることが分かりました。app.yamlはこんな感じでした。

# ... 省略 ...

handlers:
- url: /stylesheets
  static_dir: templates

- url: /.*
  script: _go_app


handlersの二つ目はいいとして、一つ目はcssファイルをHTMLテンプレートファイルと同じフォルダに入れていました。なので上記の設定にしていましたが、あるフォルダをstatic_dirに設定すると、そのフォルダ以下にあるファイルはプログラム上から読めなくなってしまうんですね。

そりゃそうか、static_dirのフォルダは静的ファイルサーバに置かれるわけですから。


こんな感じにフォルダ構成を見直しました。

hogehoge ---+--- app.yaml
            |
            +--- hogehoge --- hogehoge.go
            |
            +--- templates --- hogehoge.html
            |
            +--- statics --- style.css


静的ファイルサーバに送るフォルダはこんな風にした方がいいかも。
またapp.yamlも上記フォルダ構成にあわせて修正しました。

# ... 省略 ...

handlers:
- url: /stylesheets
  static_dir: statics  # <- ここ修正

- url: /.*
  script: _go_app


めでたし、めでたし。
久しぶりに気持ちよく床につくことが出来ます♪

Qt for Androidを試してみる

先日オープンソースカンファレンス 2012 Tokyo/Springに行って来ました。とっても楽しい一日を過ごすことが出来ました。
いつも思うのですが、こういう技術イベントって、予想もしていなかった発見があります。やっぱり足で稼ぐってあるんですね。イベントに参加していなかったら一生知らないままだったと思うことだらけです。そしてもちろん今回もたくさんの発見がありました。

その中の一つが表題の件、Qt for Androidです。Qtはご存知ですよね。KDEで採用されているアプリケーションフレームワークです。KDEの他にもUbuntu Unity 2DやGoogle EarthPhotoshopなど採用事例には事欠かない超有名なオープンソースソフトウェアです。
このQtがAndroidでも使えるようになったというから、これは試さないわけにはいかない、ということで試してみました。
感想は後で述べますが、これはやばいです。本気でQtを勉強しようと思いました。

Necessitas

開発環境を整える

とはいうもののまずは動かしたいということで開発環境を整えて行きます。そうしたらいつものようにデモを動かしてみます。
Qt for Androidの開発環境に必要なソフトウェアはこんなところです。ちなみに今回はLinux (Ubuntu) で試しています。

Necessitas SDKというのがQt for Androidの核になるモジュールです。JDKAndroid SDK/NDKのインストール方法は他のサイトにたくさん書かれているのでここではその他のモジュールのインストール方法を紹介して行きます。

Necessitas SDKをインストールする

では早速、Qt for Androidの核であるNecessitas SDKをインストールします。こちらのサイトを参考にしています。

How to install Necessitas SDK


なお、Necessitas SDKはオールインワンな環境です。事前にQt Creatorをインストールしておく必要などはありません。個人的にはQt Creatorプラグイン的にインストール出来た方がいいんですけどね。ていうか間違ってQt Creatorをインストールしてしまいました。。。後述しますが、Antも付いてきます。


まず上記のサイトからワンクリックインストーラをダウンロードします。Linux版だけじゃなく、WindowsMacもあるんですね*1。そして実行モードを与えて、実行します。

% chmod +x necessitas-0.3-online-sdk-installer-linux
% ./necessitas-0.3-online-sdk-installer-linux



実行してしばらくするとウェルカムメッセージが現れますので、迷わずNextを押しましょう。


するとインストール場所を聞いてきます。


ここはホームディレクトリ下にインストールしたいところですが、参考にしたサイトには

/opt/necessitas/ has been tested intensively, so please use this one

How to install Necessitas


とあるので泣く泣く (?) /opt/necessitas/ とします。まぁいいか。
最後にインストールするコンポーネントを選択します。


ここは注意が必要な箇所です。AndroidSDK/NDKには必ずチェックを付けたままにしておいて下さい*2。こちらにNecessitas作者のBogDon氏による投稿があります。

It seems Google removed the __atomic_xxx functions from some
platforms in NDK r7 check [1]. Meanwhile I strongly suggest you to use
the NDK from Necessitas SDK.

Link error: undefined reference to __atomic_inc


賢明な皆さんはNDKだけは少なくともチェックしておいて下さい。私はこれで一日棒に振りました。。。
また、Antはインストールしていないのでインストールしてもらいます。


それではインストール開始です。
/optディレクトリ以下にインストールするのでまずはルートパスワードを聞いてきます。


素直にルートパスワードを入れて続行。しばらく待つとインストールが完了されます。


最後にQt Creatorを立ち上げます。

Necessitasをセットアップする

一通りインストールしたらNecessitasの設定をします。こちらの内容はSetup QtCreatorを参考にしています。


まずはQt4の設定です。Tools > Optionsを選択して、Qt4タブを開きます。


Addボタンで、以下のパス (qmakeのパス) を設定します。

/opt/necessitas/Android/Qt/480/armeabi-v7a/bin/qmake


次にAndroidタブを開きます。


それぞれ、以下の設定をします*3

設定項目 設定値
Android SDK location /opt/necessitas/android-sdk
Android NDK location /opt/necessitas//android-ndk-r6b
Android NDK toolchain version 4.4.3
Ant location /opt/necessitas//apache-ant-1.8.2/bin/ant
arm GDB location /opt/necessitas/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gdb
arm GDBserver location /opt/necessitas/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/gdbserver
x86 GDB location /opt/necessitas/android-ndk-r6b/toolchains/x86-4.4.3/prebuilt/linux-x86/bin/i686-android-linux-gdb
x86 GDBserver location /opt/necessitas/android-ndk-r6b/toolchains/x86-4.4.3/prebuilt/gdbserver
OpenJDK location /usr/bin/java


これで設定終了!

Necessitasでデバッグしてみる

さて次にQtプロジェクトを作って、デバッグしてみます。目標はC++コードでブレークさせること。Let's start!

いろいろ試行錯誤してみたんですが、どうもプロジェクトはQMLがからんでいるとデバッグ出来ないみたい。実行は出来るんですがね。

ということで、試した感じでは以下のプロジェクトが大丈夫そうです。

  1. Qt Quick Application
  2. Qt Gui Application
  3. Mobile Qt Application


なお、プロジェクトを作る際、2つ注意があります。
まず一つ目はApplication Type。Built-in elements only (for all platforms) とします。


また以下に示すTarget Setupの画面では必ずQt4設定で入力したターゲットを選択して下さい。
下の例ではQt4.8.0 DebugとQt4.8.0 Releaseです。


では何はともあれリビルドしましょう。
もしエラーが出たら、ファイルのパーミッションの問題である可能性があります。
環境との相談になりますが、問題なければパーミッションを変更します。

% cd /opt/necessitas/
% sudo chmod -R 755 android-sdk
% sudo chmod -R 755 android-ndk-r6b


少なくとも私はこうしないとビルドが通りませんでした。


では早速デバッグ・・・とその前にQtアプリケーションをAndroid上で動かすにはQtライブラリが必要です。
そのQtライブラリをデバイスにインストールしてくれるツールがあります。

Ministro II


端末にインストールしておきましょう (ルート権限などは不要です) 。
インストールしたらQtライブラリをダウンロードしてくれるので指示に従います。

これで準備完了。
お待ちかねのデバッグを開始します。ブレークポイントを設定するのを忘れずに。。。


おぉ!止まった!!分かりにくいですが、main.cppの14行目でブレークしています。
とりあえずめでたしめでたし。

*1:Qt アプリケーションを Android に – クイックスタートビデオではLinuxのみ対応と書いてあったんですが、日々進化しているようですね

*2:もしかしたらSDKは要らないかも

*3:これでとりあえず動いた、という設定ですのであしからず

GITkitを使ってみる Part3

最後にOAuth2.0を使ったユーザ認証を実装していきます。結構ハマったので、自分の頭の中を整理する意味でもまとめていきます。

まずはいつものUIから見て行きます。


右側のペインはGITkitを使ってみる Part2でも書いた、そのサイト固有のアカウントでログインするためのインターフェイスです。そして左側のペインが今回実装していくOAuth2.0を使ってログインするためのインターフェイスです。まずはGMailのボタンを押してみます。


てな感じでおなじみのGMailログインの画面がポップアップで表示されます。メールアドレスとパスワードを入力してログインします。


という具合にこれまたおなじみのGoogleアカウントへのアクセス許可画面が表示されます。もちろん許可します。すると、ここでようやくアプリケーション側に制御が回ってきます。Account Chooserの初期設定で指定したcallbackUrlに対してGETもしくはPOST要求が投げられます。ここが結構難しかった。ハマってしまいました。

公式ドキュメントによると、まずはverifyAssertionというAPIを叩け、とあります。このAPI、リファレンスを確認するとWeb APIです。

identitytoolkit.relyingparty.verifyAssertion


POSTメソッドで、JSON形式で二つのパラメータを伴います。postBodyの方はcallbackUrlに渡されたHTTPリクエストのBodyをそのまま渡せばよさそうです。

def get(self):
    self.verify(self.request.body)

def post(self):
    self.verify(self.request.body)

def verify(self, body):
    pass


問題はrequestUriです。リファレンスによると

The URI to which the IDP authentication page redirects.


1日悩みました。でも意味を素直に解釈すると簡単なことでした。

def verify(self, body):
    data = {
        'requestUri': self.request.url,
        'postBody': body}


ということですね。callbackUriがコールバックされた時のURL全体のことを意味します。これでverifyAssertion APIをurlfetchサービスを使って呼び出します。

def verify(self, body):
    data = {
        'requestUri': self.request.url,
        'postBody': body}
    params = simplejson.dumps(data)
    response = urlfetch.fetch(
        url='https://www.googleapis.com/identitytoolkit/v1/relyingparty/verifyAssertion?key=Your API key',
        payload=params,
        method = urlfetch.POST,
        headers = {'Content-Type': 'application/json'})

    if response.status_code != 200:
        logging.error('Body:%s' % response.content)
        self.redirect('failed')
        return


うまく行けばステータスコード200が返ってきます。200が返ってこなくても引数 (response.content) が付いてきて、エラーの理由なんかが記載されているので、200が返らない場合は引数を出力してみるといいかもです。

ここまで行けば後は簡単・・・と言いたいところですが、ハマってしまいました。こちらは2時間ぐらい。

公式ドキュメントによれば、この後

  1. verifiedEmailが登録済みならログインさせる
  2. verifiedEmailが未登録なら (追加情報があれば) アカウント登録画面に遷移させる


とあります。今回は簡単に試したいため、必要な情報はメールアドレスだけとしているので、追加情報はありません。つまりやりたいのは、DBにアカウント情報を登録して、ポップアップウィンドウを消して、ログイン成功画面に遷移 (リダイレクト) させたいのです。それにはポップアップウィンドウ上で以下のスクリプトレンダリングします。レンダリングする、と書くと仰々しいですが、要はGET/POSTメソッドの返りとして次のHTMLファイルを返せばよいのです (これに気づくのに時間がかかったわけです、ハイ) 。

<!doctype html>
<html lang=en>
    <head>
        <meta charset=utf-8>
        <title>OAuth2.0 Result</title>
        <script type='text/javascript' src='https://ajax.googleapis.com/jsapi'></script>
        <script type='text/javascript'>
            google.load("identitytoolkit", "1.0", {packages: ["notify"]});
        </script>
        <script type='text/javascript'>
            window.google.identitytoolkit.notifyFederatedSuccess({ "email": "{{ email }}", "registered": true });
            // use window.google.identitytoolkit.notifyFederatedError(); in case of error
        </script>
    </head>
    <body>
    </body>
</html>


emailはテンプレートパラメータとして渡してあげる必要があります。こんな感じ。

def verify(self, body):
    # ... 省略 ...

    query = UserInformation.all().filter('email = ', email)
    if query == None or query.count() != 1:
        record = UserInformation(email=email, isAuth2=True)
        record.put()

    params = {"email" : email}
    path = os.path.join(os.path.dirname(__file__), "oauth2.html")
    self.response.out.write(template.render(path, params))


そうすると、ポップアップウィンドウは消え、/homeにリダイレクトされました。

めでたし、めでたし。

GITkitを使ってみる Part2

Part1では単純にAccount Chooserを立ち上げたに過ぎませんでした。きちんとユーザ認証出来るように改造していきます。

OAuth2.0を使わないユーザ認証

GITkitを使うといっても、必ずGoogleYahoo! (以下IDP...IDentitiy Provider) のメールアドレスじゃないとユーザ登録出来ないのではちょっと不便です。そのサイト固有のユーザ情報を登録したいときもあります。そこでもう一度Account Chooserのユーザインターフェイスを見てみます。


左側のペインはIDPを使ったログインのためのインターフェイスです。そして右側がサイト固有のアカウントでログインするためのインターフェイスです。今回は右側を作って行きます。


GITkitでは、Javascriptで設定したコールバックURLをハンドリングすることでユーザ認証を実現します。つまりGITkit側が既定のコールバックURLに対してGETやらPOSTを投げてくるのです。これらのリクエストに適宜返答していくことで全体のユーザ認証を実現することが出来るわけです。

それでは一つ一つ見ていくことにします。

/usrStatusUrl

こちらのコールバックURLはずばり、Account Chooserの右側にメールアドレスを入力し、ログインボタンを押した時にコールバックされてきます。ここでは引数として渡ってくるメールアドレスが登録済みのものかどうかをチェックして、結果をJSON形式で返します。データストアを使って超簡単に作ってみました。

class UserInformation(db.Model):
    email = db.StringProperty(required=True)
    password = db.StringProperty(required=True)

# /userStatusUrlのコールバックハンドラ
class UserStatusPage(webapp.RequestHandler):
    def post(self):
        email = self.request.get('email')
        query = UserInformation.all().filter('email = ', email)
        if query == None or query.count() != 1:
            response = {"registered" : False}
        else:
            response = {"registered" : True}

        self.response.content_type = 'application/json'
        simplejson.dump(response, self.response.out, ensure_ascii=False)


データストアに指定されたメールアドレスを照会しています。もし存在していれば、「registered: True」で返します。存在していなければ「registered: False」で返します。もしTrueを返すと以下のようなパスワードプロンプト画面が表示されます。


そしてもしFalseを返すと /signup がコールバックされます。

/login

↑で書いたパスワードプロンプトにパスワードを入れてもらい、ログインすると /login がコールバックされます。普通であればここはhttpsを使うんでしょうね (そうしないとパスワードが平文で流れてしまうので) 。引数として受け取ったメールアドレスとパスワードを照会して、結果をJSON形式で返却します。

こちらも先ほどと同様、データストアを使って簡単に実装しています。

class MyLoginPage(webapp.RequestHandler):
    def post(self):
        email = self.request.get('email')
        password = self.request.get('password')

        query = UserInformation.all().filter('email = ', email).filter('password = ', password)
        if query == None or query.count() != 1:
            response = {'status' : 'PasswordError'}
        else:
            response = {'status' : 'OK'}

    self.response.content_type = 'application/json'
    simplejson.dump(response, self.response.out, ensure_ascii=False)


一致するユーザ情報があれば、「status: OK」を返します。もしなければ (パスワードが間違っていれば) 「status: PasswordError」を返します。とっても簡単な作りになっています。ここでPasswordErrorを返すとこんな感じに表示されます。


Trueを返すと /home にリダイレクトされます。

/home

こちらはサイト固有のアカウントでログインが成功した後リダイレクトされるURLです。

/signup

このURLは入力したメールアドレスがまだ登録されていない場合にコールバックされます。そこでパスワードや他の情報を入力してもらうことになります。


サイト固有のユーザ認証を実現するのはとても簡単でした。次は (当然ですが) IDPを使ったユーザ認証に挑戦します。

日本Androidの会 1月定例会

今月も行ってきました、定例会。帰り道はかなり冷えました。
いろいろ面白いネタがあったので、簡単にご紹介。

ADKで広がるAndroidアプリの可能性

言わずと知れたRT Corporation*1の社長さんによるセッション。


ADKとは何ぞや?RT-ADK/RT-ADSのお話などがありました。この辺は会社で調べたことがある内容なので (Arduino + USB Hostシールドで実験したこともあるので) 目新しい内容はありませんでした。

5月ごろにCQ出版からRT-ADK mini付きのADK勉強キットが発売されるそうです。ADKを自作するのが面倒くさいかたには朗報ですね。※自作も簡単ですけどね。


セッション終了後に、製品化事例についての質問がありました。回答としてはまだないとのこと。質問された方の話によるとCTS (Compatibility Test Suite) によればOpen AccessoryはMUSTではないため、企業もOpen Accessoryを使ったソリューションを出しにくいのでは?という意見が出ていました。なるほど、確かにその通りですね。

Google TVとApple TV

定例会でよくセッションされているGClueの社長さんによるセッション。RTの社長さんもそうですが、エンジニア系会社の社長さんはすごい!


先日開催されたCESで発表された様々なGoogle TVについてお話がありました。なんでも今年はSmart TV第二世代だとか。ちなみに去年に発売されたSmart TVが第一世代。

お話の中で衝撃的だったことがありました。これまでGoogle TV上のアプリは全てWebアプリでした。つまりアプリ=ブックマークみたいな感じ。ですが今年 (第二世代) のGoogle TVはネイティブアプリが主な形態となっているそうです。第一世代ではAndroid Marketを利用出来なかったというのもあるんでしょうが。そりゃやっぱりネイティブアプリの方がいいでしょ。

第二世代と言われるゆえんでしょうかね。第一世代と第二世代とは完全に別物のようだ、とのこと。Google TVも進化していってるんですね。STICK型のGoogle TVはかなり欲しいです (笑) 。


Apple TVに関してはちょっとした紹介程度でした。


Smart TVとは関係ないのですが、スピーカーの方がこの度日本Androidの会の部として、「オープンナビ」部会を立ち上げることになったそうです。ここで言う「ナビ」とはカーナビのことです。1/31に詳細発表があるそうなのでこれはウォッチしておかないと。

2012 International CES + AT&T Developer Summit報告

日経BPの菊池氏 (この方も度々登壇されます) によるセッション。ご本人も仰ってましたが、内容が盛りだくさんでした。ここでは私が興味を持った箇所だけをかいつまんで記載します。

Samsung & LGの勢い

これはもう言うまでもないでしょうね。韓国メーカの強いこと強いこと。

自動車メーカの出展

フォードはもちろんですが、ベンツやAudiなどの自動車メーカも出展していたとのこと。社内で利用するユーザインターフェイスをスマフォに学べ、ってことなんだそうです。

*1:RT-ADK/RT-ADSというAndroidとUSBでつなげるボードを製作・販売していることで有名

GITkitを使ってみる Part1

GDD2011のBreakoutセッションでOAuth2.0やOpenIDなどのユーザ認証に関する内容がありました。

Google Developer Day 2011 Japan: 「データアクセスと本人認証のための OAuth と OpenID」


OAuth2.0をとても分かりやすく (日本語で!) 説明しているセッションです。興味のある方はぜひ見てみて下さい。
このセッションの中で触れられていたものに、Account ChooserやGITkitというものがありました。OAuth2.0を使ったユーザ認証を行う場合の、最新のユーザインターフェイスとのこと。
結構いい感じのUIだったので、ぜひとも試してみたいと感じていました。・・・で今回App Engineを使って手を付け始めたわけです。

参考にしたサイトはこちら。
Google Identity Toolkit


少しずつ進めていきたいと思います。

とりあえず表示してみる

感じをつかむだけであれば、こちらのサイトが秀逸です。

Basic Demo Store
Demo RP Site using GIT


どちらも左上のSign inボタンを押すとAccount Chooserを見ることが出来ます。

ではこのUIを自分のアプリで実現します。とりあえず表示するだけなら、結構簡単に出来ます。

  1. API ConsoleでGITkit用のプロジェクトを作成
  2. GITkitプロジェクトで生成されたスクリプトをHTMLファイルにコピペ


表示するだけならほんと簡単です。でも実際にユーザ認証する場合はもうちょっと面倒です。

私が実験したコードはこんな感じ。

# -*- coding: utf-8 -*-
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app

import os

class MainPage(webapp.RequestHandler):
    def get(self):
        path = os.path.join(os.path.dirname(__file__), "login.html")
        self.response.out.write(template.render(path, {}))

application = webapp.WSGIApplication([('/', MainPage)], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()


単にHTMLファイルを返しているだけのアプリです。HTMLファイル (テンプレート) はこんな感じ。

<!doctype html>
<html lang=en>
    <head>
        <meta charset=utf-8>
        <title>GITkit使ってみる!</title>
        
        <!-- ここからAPI Consoleで自動生成されたスクリプト -->
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/googleapis/0.0.4/googleapis.min.js"></script>
        <script type="text/javascript" src="//ajax.googleapis.com/jsapi"></script>
        <script type="text/javascript">
            google.load("identitytoolkit", "1", {packages: ["ac"], language:"ja"});
        </script>
        <script type="text/javascript">
        $(function() {
            window.google.identitytoolkit.setConfig({
                developerKey: "ここにAPIキーが入ります",
                companyName: "Hogehoge",
                callbackUrl: "http://localhost:8080/callback",
                realm: "",
                userStatusUrl: "/userStatus",
                loginUrl: "/login",
                signupUrl: "/signup",
                homeUrl: "/home",
                logoutUrl: "/signout",
                idps: ["Gmail", "Yahoo"],
                tryFederatedFirst: true,
                useCachedUserStatus: false
            });
            $("#navbar").accountChooser();
        });
    </script>
    <!-- ここまでAPI Consoleで自動生成されたスクリプト -->

    </head>
    <body>
        <div id="navbar">ログイン</div>
    </body>
</html>


↑中の

<div id="navbar">ログイン</div>


で例のボタンが表示されます。
各種URLのハンドラを設定する必要がありますが、とりあえず表示するのであればこれでOK。早速実行してみます。


すかさずログインボタンを押してみます。


おぉ〜出た出た。めでたしめでたし。
次は各種URLハンドラを実装してみます。