2011/02/02

Android で Googleドキュメントの一覧を取得する

@ Xperia (Android 2.1 Eclair)

Googleドキュメントやその他のGoogleのサービスは、その多くがHTTPアクセスでXMLを取得できるAPIを用意しているため、比較的容易にその通信を実装することができます。

↓ Google Docs のプロトコル
http://code.google.com/intl/en/apis/documents/docs/3.0/developers_guide_protocol.html

しかし、定型文のプロトコルを毎回ユーザが実装するのは面倒くさい。ってことで、Google は各言語からアクセス可能なラッパーAPIを用意しています。

↓ Google Data API
http://code.google.com/intl/en/apis/gdata/docs/client-libraries.html

ちょっと前まではこの Google Data API で Android用のAPIも定義されていたのですが、最近になって分離したみたいです。というわけで、上記の Google Data API では Android用のプログラムは書けません。
トライはしてみたのですが、どうもうまくいきませんでした。API としては結構良い出来なんですがねぇ。
とはいえ、GoogleがAndroidを見捨てるはずがありません。現在は以下のAPIを使うことができます。

↓ Google API Client Library for Java
http://code.google.com/p/google-api-java-client/

というより、今後Java用のAPIはこちらに移行するんでしょうかね。 このAPIはまだアルファバージョンのため、まだ不完全感が漂っていますが、使ってみると結構良い感じです。
以下にAndroidアプリケーションから Google API Client Library for Java を使って Googleドキュメント の一覧を取得するサンプルを挙げておきます。
  1. いつも通り Eclipse上で Android プロジェクトを作成します
  2. Google API Client Library for Java から google-api-client-x.x.x-java.zip をダウンロードして解凍します。今回は、google-api-client-1.2.2-alpha-java.zip を使用しました。
  3. プロジェクト直下に lib ディレクトリを作成し、先のライブラリをおきます。少なくともこのバージョンでは google-api-client-1.2.2-alpha.jar という1ファイルにまとめられているようです。
    (その他のファイルはソースコード等です。デバッグには使えますが、コンパイルするだけなら必要ありません。)
  4. Eclipse上でプロジェクトをリフレッシュ(F5)します。lib ディレクトリが表示されるので、ライブラリを選択して、
    右クリック→ビルド・パス→ビルド・パスに追加
    としてライブラリを登録します。
  5. MainActivity.java、 AndroidManifest.xml を以下のように修正します。
    MainActivity.java
    package com.kokufu.test;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    import android.accounts.Account;
    import android.accounts.AccountManager;
    import android.accounts.AuthenticatorException;
    import android.accounts.OperationCanceledException;
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.ViewGroup.LayoutParams;
    import android.widget.TextView;
    
    import com.google.api.client.googleapis.GoogleHeaders;
    import com.google.api.client.googleapis.GoogleTransport;
    import com.google.api.client.googleapis.GoogleUrl;
    import com.google.api.client.http.HttpRequest;
    import com.google.api.client.http.HttpTransport;
    import com.google.api.client.util.Key;
    import com.google.api.client.xml.XmlNamespaceDictionary;
    import com.google.api.client.xml.atom.AtomParser;
    
    public class MainActivity extends Activity {
        private static final String TAG = "GoogleDocsTest";
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            // AccountManager を通じてGoogleアカウントを取得
            AccountManager manager = AccountManager.get(this);
            Account[] accounts =
                        manager.getAccountsByType("com.google");
            Bundle bundle = null;
            try {
                bundle = manager.getAuthToken(
                        accounts[0], // テストなので固定
                        "writely",   // ※1
                        null,
                        this,
                        null,
                        null).getResult();
            } catch (OperationCanceledException e) {
                Log.e(TAG, "", e);
                return;
            } catch (AuthenticatorException e) {
                Log.e(TAG, "", e);
                return;
            } catch (IOException e) {
                Log.e(TAG, "", e);
                return;
            }
    
            String authToken = "";
            if (bundle.containsKey(AccountManager.KEY_INTENT)) {
                // 認証が必要な場合
                Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
                int flags = intent.getFlags();
                flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
                intent.setFlags(flags);
                startActivityForResult(intent, 0);
                // 本当はResultを受けとる必要があるけど割愛
                return;
            } else {
                // 認証用トークン取得
                authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
            }
    
            // 送信準備
            HttpTransport transport = GoogleTransport.create();
            GoogleHeaders headers = (GoogleHeaders) transport.defaultHeaders;
            headers.setApplicationName("Kokufu-GoogleDocsTest/1.0");
            headers.gdataVersion = "3";
            headers.setGoogleLogin(authToken); // 認証トークン設定
    
            // Parser を準備して Transport にセットする
            AtomParser parser = new AtomParser();
            // 空の Dictionary でとりあえず問題なさげ
            parser.namespaceDictionary =
                       new XmlNamespaceDictionary();
            transport.addParser(parser);
    
            // 送信
            Feed feed = null;
            try {
                HttpRequest request = transport.buildGetRequest();
                request.url = new GoogleUrl("https://docs.google.com/feeds/default/private/full"); // ※2 
                feed = request.execute().parseAs(Feed.class);
            } catch (IOException e) {
                Log.e(TAG, "", e);
                return;
            }
    
            // 結果を表示
            String tmp = "";
            for (Entry entry : feed.entries) {
                tmp += entry.title + "\n";
            }
            TextView v = new TextView(this);
            v.setText(tmp);
            this.addContentView(
                    v,
                    new LayoutParams(LayoutParams.WRAP_CONTENT,
                                     LayoutParams.WRAP_CONTENT));
        }
    
        /**
         * Feed タグ
         */
        private class Feed {
            @Key("entry")
            public List entries = new ArrayList();
        }
    
        /**
         * Entry タグ
         */
        private class Entry {
            @Key
            public String summary;
    
            @Key
            public String title;
    
            @Key
            public String updated;
          }
    }
    

    AndroidManifest.xml に以下のパーミッションを追加
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    <uses-permission android:name="android.permission.INTERNET" />
    
onCreate の中に全部書いちゃうという酷いプログラムですが…まぁ可読性は良いってことで。これくらいシンプルにかけちゃうっていうのが、ライブラリを使う魅力ですね。

わかりやすさを重視して書いているので、Google Account が登録されていないような端末で動作させるとアプリが落ちます。その他、通信状況が悪かったりしてもうまくいかないことがあると思いますが、その辺ご容赦ください。

また、このやり方で、Google Docs 以外のGoogleサービスにもほとんどアクセスが可能です。
ポイントは ※1 に書いてある"writely"という文字列(authTokenType)。この"writely"が、Google Docsに対するアクセス権の認証を意味しています。
その他の authTokenType 一覧は無いものかと探してみたら、まさにタイムリー!!
Mine さんの AndroidのGoogle Authenticatorを解析(?)してみた という投稿に詳しく書いてあります。
素晴らしい!

また、※2の Query についてはGoogle Docs のプロトコル
http://code.google.com/intl/en/apis/documents/docs/3.0/developers_guide_protocol.html
に詳しく載っています。
この辺を読まなくても良いくらいに隠蔽してくれるとAPIとして使いやすいんですけどねぇ。
まぁ、アルファ版なんで仕方がありません。API仕様もこれからどんどん変わっていく可能性があるので、この記事の賞味期限は短めだろうなぁ…

0 件のコメント: