Wednesday, March 30, 2011

Google Spreadsheets を Androidから扱う (1)

Androidアプリで扱うデータを外部のSpreadsheetに保存することを目的とします。

library

今回はGoogle API Client Library for Javaの1.3.1-alphaを使ってみます。
とりあえず,プロジェクトに次の3つのjarファイルを参照するように設定いたしました。
  • google-api-client-1.3.1-alpha.jar
  • google-api-client-googleapis-1.3.1-alpha.jar
  • dependencies/guava-r08.jar
google-api-client-extensions-1.3.1-alpha.jarは Google App Engine用なので,今回は含めません。依存関係は試行錯誤の結果です。dependecies以下のjar(soruceは除く)を予め含めた方が今後楽かもしれません。

認証

Spreadsheetなど扱う前に,アプリケーションへ認証を与えることが必要になります。 ここではOAuthを用いた認証を行います。
具体的な手順は, OAuth 1.0a for Google API's JavaDocに記載されています。
まずは,temporary credentials token ("request token") を入手します。 installed application では signature method に "HMAC-SHA1",consumerKeyとclientSharedSecretに"anonymos"を使うように求められていますので,それに従います。
    HttpTransport transport = new ApacheHttpTransport();
    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;
displayNameには,このアプリケーションの名前(String 型)を入れます。scopeに入るべき値は,Google Data Protocol FAQを参照してください。
それでは,TemporaryTokenの値を入手してみます。
    OAuthCredentialsResponse credentials = temporaryToken.execute();
戻り値のtoken値を利用して,GoogleOAuthAuthorizeTemoraryTokenUrlを作成します。これで,ユーザにアクセスしていただくurlが得られます。
    if (credentials != null) {
        GoogleOAuthAuthorizeTemporaryTokenUrl temporaryTokenUrl = new GoogleOAuthAuthorizeTemporaryTokenUrl();
        temporaryTokenUrl.template = "mobile";
        temporaryTokenUrl.temporaryToken = credentials.token;
        // Uri uri = new Uri(temporaryTokenUrl.build());
        Log.d(TAG, "temoraryTokenUrl: " + temporaryTokenUrl.build());
    }
実行すると, temoratyTokenUrl の値として,
https://www.google.com/accounts/OAuthAuthorizeToken?btmpl=mobile&oauth_token=...some_oauth_token....
が得られました。
この次は,このURLをWebViewに表示させまして,アプリケーションの認証を行います。

Monday, March 28, 2011

ContentProviderのテストの仕方

今回,BottomPriceProvider というContentProviderの単体テストを行なう BottomPrivceProviderTest を作成してみましたので,そのメモを記していきます。

クラスの作成

ProviderTestCase2>T extends android.content.ContentProvider<を継承して作成します。
public class BottomPriceProviderTest extends ProviderTestCase2 {
...
}

コンストラクタの生成

super class から コンストラクタを自動生成すると,次のようなコードが生成されます。
 public BottomPriceProviderTest(Class providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
        // TODO Auto-generated constructor stub
    }
面倒なので,引数を指定しない版も作成しておきます。
 public BottomPriceProviderTest() {
        super(BottomPriceProvider.class, BottomPriceProvider.AUTHORITY);
        // TODO Auto-generated constructor stub
    }
ここでの,BottomPriceProvider.AUTHORITY は ContentProviderを提供する際に指定するContentのAuthority名です。

setUp, tearDown

setUpでは,フィールド mMockContext にgetMockContext()で得られるContextを格納しておきます。
tearDownでは特になにもしません。
    private Context mMockContext;
    @Override
    protected void setUp() throws Exception {
        // TODO Auto-generated method stub
        super.setUp();
        mMockContext = getMockContext();
    }

    @Override
    protected void tearDown() throws Exception {
        // TODO Auto-generated method stub
        super.tearDown();
    }

テスト

まずは簡単なところで,insert/query/deleteのテストを書いてみます。 テスト用のContentValuesを挿入,検索し,その検索結果が挿入したContentValuesの内容と一致することを確認し,最後にテスト用ContentValuesを削除します。
    public void testInsertDeleteRecord() {
        final ContentResolver resolver = mMockContext.getContentResolver();
        final Uri uri = BottomPrice.Prices.CONTENT_URI;
        ContentValues cv = new ContentValues();
        int price = 480;
        String jan_code_str = "4901111725133";
        cv.put(BottomPrice.Prices.PRICE, price);
        cv.put(BottomPrice.Prices.JAN_CODE, jan_code_str);
        Uri insertUri = resolver.insert(uri, cv);
        String price_id = insertUri.getPathSegments().get(1);
        
        // Query
        Uri queryUri = Uri.withAppendedPath(uri, price_id);
        String[] projection = new String[] {
                BottomPrice.Prices._ID,
                BottomPrice.Prices.PRICE,
                BottomPrice.Prices.JAN_CODE,
            };
        Cursor c = resolver.query(queryUri, projection, null, null, null);
        assertEquals("query resuult", 1, c.getCount());
        c.moveToFirst();
        assertEquals("Jan Code", jan_code_str, c.getString(c.getColumnIndex(BottomPrice.Prices.JAN_CODE)));
        assertEquals("Price", price, c.getInt(c.getColumnIndex(BottomPrice.Prices.PRICE)));
        c.close();
        
        resolver.delete(queryUri, null, null);
    }

実際にテストを実行してみましょう。
テスト Errorとなり,Failure Trace をみると
android.database.SQLException: Failed to insert row into content://com.damburisoft.android.app.bottomrpice.provider.bottompriceprovider/prices
at com.damburisoft.android.app.bottomrpice.provider.BottomPriceProvider.insert(BottomPriceProvider.java:107)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:150)
at android.content.ContentResolver.insert(ContentResolver.java:629)
at com.damburisoft.android.provider.BottomPriceProviderTest.testInsertDeleteRecord(BottomPriceProviderTest.java:50)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:430)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
とあるので,これを直していくことによって自分のContentProviderを創り上げていきます。

Sunday, March 27, 2011

今後の方針 (Mar/27/2011 version)

自分の方針というのを決めていこうと考える。ただ,一度決めた物絶対ということではなく,何度も推敲を重ねていこうと。

  • ◯◯とITを念頭に。ITの中だけで閉じることに何らかの疑問がある。ITを通して現実の世界にある問題を解決する手助けをしよう。
  • 80%の力で
    重要な時に歯を食いしばるのはいいが,常に歯を食いしばっている状況はおかしいはず。全力で90分フルに走れるフットボール選手はいないように,要素要素で力を抜けるように。
  • 人は迷惑をかけて生きている。迷惑をかけているかどうか気にしない。自分が無理なく生きていくためにやれることをまず探す。