Thursday, April 07, 2011

Google Spreadsheets を Androidから扱う (4)

前回,access Token と access TokenSecretを取得しました。 これを用いて,自分のアカウントのspread sheet一覧を取得してみます。
Retrieving a list of spreadsheetsを読みますと,
https://spreadsheets.google.com/feeds/spreadsheets/private/full
に送信すれば,認証されたユーザのspreadsheetsのfeedを入手することができます。
具体的なコードは以下の通りです。

HttpTransporへの署名

String token = "your access token";
String tokenSecret = "your access tokenSecret";
HttpTransport transport = new ApacheHttpTransport();
OAuthParameters parameters = createOAuthParameters(token, tokenSecret);
parameters.signRequestsUsingAuthorizationHeader(transport);
getSpreadSheets(transport);
各メソッドの内容は以下の通りです。
createOAuthParameters
public OAuthParameters createOAuthParameters(String token, String tokenSecret) {
    OAuthParameters authorizer = new OAuthParameters();
    authorizer.consumerKey = "anonymous";
    authorizer.signer = createOAuthSigner(tokenSecret);
    authorizer.token = token;
    return authorizer;
}
createOAuthSigner
public OAuthHmacSigner createOAuthSigner(String tokenSecret) {
    OAuthHmacSigner signer = new OAuthHmacSigner();
    if (tokenSecret != null) {
        signer.tokenSharedSecret = tokenSecret;
    }
    signer.clientSharedSecret = "anonymous";
    return signer;
}
getSpreadSheet
public void getSpreadSheets(HttpTransport transport) {
    // Set AtomParser
    AtomParser parser = new AtomParser();
    XmlNamespaceDictionary dictionary = new XmlNamespaceDictionary();
    dictionary.set("", Atom.ATOM_NAMESPACE);
    dictionary.set("docs", "http://schemas.google.com/docs/2007");
    dictionary.set("batch", "http://schemas.google.com/gdata/batch");
    dictionary.set("gd", "http://schemas.google.com/g/2005");
    dictionary.set("gd:etag", "W/"DEMCQHk7eSt7ImA9WhZSGUw."");
    dictionary.set("openSearch", "http://a9.com/-/spec/opensearch/1.1/");
    dictionary.set("app", "http://www.w3.org/2007/app");
    parser.namespaceDictionary = dictionary;
    transport.addParser(parser);

    // Set Http Headers
    HttpHeaders headers = transport.defaultHeaders;
    headers.set("User-Agent", appname);
    headers.set("GData-Version", "3.0");

    HttpRequest request = transport.buildGetRequest();
    request.url = new GoogleUrl("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
    try {
        HttpResponse response = request.execute();
        // null chekc していないので注意
        InputStreamReader inr = new InputStreamReader(response.getEntity().getContent()); 
        BufferedReader reader = new BufferedReader(inr, 256);
        while ((str = reader.readLine()) != null) {
            Log.d(TAG, line);
        }
        reader.close();
        inr.close();
    } catch (IOException e) {
        Log.e(TAG, e.getMessage());
        e.printStackTrace();
    }
}
これを実行し,正常にHttpResponseが得られますと,以下のようなXML documentが得られます。
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns:gd='http://schemas.google.com/g/2005' gd:etag='W/"DEMCQHk7eSt7ImA9WhZSGUw."'>
  <id>https://spreadsheets.google.com/feeds/spreadsheets/private/full</id>
  <updated>2011-04-04T11:27:41.701Z</updated>
  <category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/>
  <title>Available Spreadsheets - Maskes(user's email address)</title>
  <link rel='alternate' type='text/html' href='http://docs.google.com'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/spreadsheets/private/full'/>
  <link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/spreadsheets/private/full'/>
  <openSearch:totalResults>11</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <entry gd:etag='"BBQbQxQLQit7ImBr"'>
    <id>https://spreadsheets.google.com/feeds/spreadsheets/tRsAk0wSkUi4wCBZxOJITuw</id>
    <updated>2011-04-04T04:52:59.849Z</updated>
    <category scheme='http://schemas.google.com/spreadsheets/2006' term='http://schemas.google.com/spreadsheets/2006#spreadsheet'/>
    <title>Hack For Japan Projects</title>
    <content type='application/atom+xml;type=feed' src='https://spreadsheets.google.com/feeds/worksheets/tRsAk0wSkUi4wCBZxOJITuw/private/full'/>
    <link rel='http://schemas.google.com/spreadsheets/2006#tablesfeed' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/tRsAk0wSkUi4wCBZxOJITuw/tables'/>
    <link rel='alternate' type='text/html' href='https://spreadsheets.google.com/ccc?key=0Amb6cvTCzTQRdFJzQWswd1NrVWk0d0NCWnhPSklUdXc'/>
    <link rel='self' type='application/atom+xml' href='https://spreadsheets.google.com/feeds/spreadsheets/private/full/tRsAk0wSkUi4wCBZxOJITuw'/>
    <author>
      <name>Masked</name>
      <email>Masked</email>
    </author>
  </entry>
</feed>
次回は,得られた XML Document の parse を行います。

Tuesday, April 05, 2011

Google Spreadsheets を Androidから扱う (3)

前回,Appを認証した際に得られるoauth_verifierとoauth_tokenの値が返ってくることまで確認しました。

今回は,これらの値と前々回,Request Toke取得途中に得られた,OAuthCredentialsResponseの値と一緒に,AccesssTokenを取得します。

oauth_verifier, oauth_tokenの取得

ブラウザからのcallback時に発行されるIntentの中のURIデータを取得し,それをOAuthCallbackUrl に引き渡します。

Uri uri = this.getIntent().getData();
OAuthCallbackUrl callbackUrl;
if (uri != null) {
    Log.d(TAG, "uri: " + uri.toString());
    callbackUrl = new OAuthCallbackUrl(uri.toString());
}

GoogleOAuthGetAccessTokenの作成

上で作成した,OAuthCallbackUrl callbackUrl と,Request Token取得時に得られた OAuthCredentialsResponse credentials (Request Token)を用いて GoogleOAuthGetAccessToken を作成します。

    HttpTransport transport = new ApacheHttpTransport();
    GoogleOAuthGetAccessToken accessToken = new GoogleOAuthGetAccessToken();
    accessToken.transport = transport;
    accessToken.temporaryToken = callbackUrl.token;
    accessToken.verifier = callbackUrl.verifier;
    accessToken.signer = createOAuthSigner(credentials);
    accessToken.consumerKey = "anonymous";

Access Tokenの取得

GoogleOAuthGetAccessToken accessTokenをexecute()することによって,Access Tokenを取得します。


    OAuthCredentialsResponse response = accessToken.execute();

この返り値 OAuthCredentialsResponse response に含まれている, (access) token と(access) tokenSecret が,今後Spreadsheetsにアクセスするための認可されたHTTP requestを生成する際に必要となります。

次回は,自分のアカウントのSpreadSheet一覧を取得します。

Monday, April 04, 2011

Google Spreadsheets を Androidから扱う (2)

OAuthの続きです。

Implict intent

androidには,intentでactivity, service, broadcastに応答させる昨日があります。この機能を利用して,Browser上でリンクをクリックして,インストールされているactivityを起動する方法があります。
とくに,
Consider, for example, what the browser application does when the user follows a link on a web page. It first tries to display the data (as it could if the link was to an HTML page). If it can't display the data, it puts together an implicit intent with the scheme and data type and tries to start an activity that can do the job. If there are no takers, it asks the download manager to download the data. That puts it under the control of a content provider, so a potentially larger pool of activities (those with filters that just name a data type) can respond.
とありますように,ブラウザでWebページ上のリンクをクリックして, データが表示されない場合,該当する暗黙のintentを発行するようになっています。 OAuth認証では,この機能を利用して,callback urlに自分のアプリを起動できるようなurlを指定します。
Google I/O 2010 Buzz Android Sample for Java Client Library for Google API's version 2.2.0-alphaの実装を参考にして,話を進めていきます。

Request Token (Temporary credentials token) の取得

前回,GoogleOAuthAuthorizeTemporaryTokenUrlを作成いたしましたが,その補足です。
Request Tokenを取得するために,GoogleOAuthGetTemporaryTokenを利用します。
    GoogleOAuthGetTemporaryToken temporaryToken = new GoogleOAuthGetTemporaryToken();
    OAuthHmacsinger signer = new OAuthHmacSigner();
    signer.clientSharedSecret = "anonymous";
    temporaryToken.signer = signer;
    temporaryToken.displayName = appname;
    temporaryToken.consumerKey = "anonymous";
    temporaryToken.scope = "https://spreadsheets.google.com/feeds/";
    temporaryToken.transport = transport;
    temporaryToken.callback = "x-myspreadsheet://com.example.abekatsu/";
前回との違いは,callbackの指定です。custom schemeを使うなという意見がありますが,custom schemeを用いたアプリの起動が広まっている現状を踏まえ,今回は利用します。
競合を避けるために,host部分をjavaのpackage名のように,自分自身のFQDNをTop levelから書いてみました。

GoogleOAuthAuthorizeTemporaryTokenUrlの作成

    OAuthCredentialsResponse response = temporaryToken.execute();
    GoogleOAuthAuthorizeTemporaryTokenUrl authorizeUrl = new GoogleOAuthAuthorizeTemporaryTokenUrl();
    authorizeUrl.template = "mobile";
    authorizeUrl.set("scope", scope);
    authorizeUrl.set("domain", "anonymous");
    authorizeUrl.set("xoauth_displayname", appname);
    authorizeUrl.temporaryToken = response.token;
この authorizeUrl で得られるURLを引数にしてブラウザを起動します。
     Intent intent = new Intent(Intent.ACTION_VIEW);
     intent.setData(Uri.parse(temporaryTokenUrl.build()));
     startActivity(intent);


実際に実行しますと,GoogleのAccountページが表示されます。ここで,「Grant access」をクリックすると,上記のcallbackで指定したURLを含む暗黙のIntentが発行されます。
















暗黙のIntentの取得

先程のステップで得られた,暗黙のIntentを取得してみましょう。
AndroidManifest.xml
まずは,AndroidManifest.xmlで,このIntentを補足するように指定します。
    
        
            
                
                
            
            
            
                
                
                
                
            
        
    
暗黙のIntentでは,カテゴリーが "android.intent.category.DEFAULT" となります。
Activityの編集
ここでは,単純に Intent に含まれているデータを表示させてみます。
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        boolean isViewAction = Intent.ACTION_VIEW.equals(getIntent().getAction());
        if (isViewAction) {
            Uri uri = this.getIntent().getData();
            if (uri != null) {
                Log.d(TAG, "uri: " + uri.toString());
            }
        }
    }
実際に,アプリケーションを実行して Grant access すると次のデータを含む 暗黙のIntentが発行されます。
D/OkodukaiNoteActivity(  235): uri: x-okozukai:///?oauth_verifier=ここにoauth_verifierの文字列&oauth_token=ここにoauth_tokenの文字列
次回はこのauth_tokenをGoogleにauthorizeします。