Android から Dropbox を利用したアプリを作成してみる

以下を参考に。

https://www.dropbox.com/developers/start/setup#android

API リファレンス https://www.dropbox.com/static/developers/dropbox-android-sdk-1.3.1-docs/index.html

Dropbox アカウントの開設

まず、Dropbox アカウントが開設されていないと話になりません。

ここ  から開設できます(なおかつ、ここからアカウント登録すると、500MB の容量ボーナスがもらえます!)

Android SDK のセットアップ

My Apps ページから、新規アプリケーションを作成

アプリケーションから利用するために、アプリケーションの情報を登録する必要がある。

https://www.dropbox.com/developers/apply

以下の手順で、app キー と app シークレットキー が取得でき、API が利用可能となる。

android_dropbox01 

Create App ボタンを押下すると、アプリケーション名と、以下のタイプを選択するダイアログが表示される

選択する内容は、、、

App フォルダ

ユーザーのDropbox内の、一つのフォルダにしか、アクセスする必要が無い場合(推奨)

フル Dropbox

ユーザーのDropbox 全体にアクセスする場合

あとは、ウィザードに従い、内容を設定。

SDKのダウンロード

SDK を https://www.dropbox.com/developers/reference/sdk からダウンロード

android_dropbox02

Dropbox + アイコンを押すと、自分のDropbox フォルダに、dropbox-android-sdk-1.3.1 フォルダが作成された。

中に、サンプルも入っているので、参考になる。

ライブラリの設定

Dropbox\dropbox-android-sdk-1.3.1\lib にある、jar を、プロジェクトの直下に、libs フォルダーを作成し、そこにコピー

プロジェクトのプロパティから、ライブラリをビルドパスに追加する。

android_dropbox03

実装

app キー と app シークレットキー など、定数を切っておく。

public class FlashcardRoidApplication extends Application {
    // app キー
    public static final String DROPBOX_APP_KEY = "xxxxxxxxxxxxx";            
    // app シークレットキー
    public static final String DROPBOX_APP_SCECRET = "xxxxxxxxxxxxx";
    public static final String DROPBOX_APP_FOLDER_NAME = "FlashcardRoid";
    public static final AccessType DROPBOX_ACCESS_TYPE = AccessType.APP_FOLDER;
}

AndroidManifest.xml に Dropbox 認証用のアクティビティを登録

<activity
  android:name="com.dropbox.client2.android.AuthActivity"
  android:launchMode="singleTask"
  android:configChanges="orientation|keyboard">
  <intent-filter>
    <!-- 以下のxxxxxxに app キー を設定する -->
    <data android:scheme="db-xxxxxx" />
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE"/>
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

パーミッションを追加

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

認証するまでのコード

package info.typea.flashcard;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.android.AndroidAuthSession;
import com.dropbox.client2.session.AccessTokenPair;
import com.dropbox.client2.session.AppKeyPair;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class FlashcardRoidActivity extends Activity {
    private DropboxAPI mDBApi;
    
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        AndroidAuthSession session = buildDropboxSession(); 
        mDBApi = new DropboxAPI(session);
        
        mDBApi.getSession().startAuthentication(FlashcardRoidActivity.this);
    }


    @Override
    protected void onResume() {
        super.onResume();
        AndroidAuthSession session = mDBApi.getSession();
        if (session.authenticationSuccessful()) {
            try {
                mDBApi.getSession().finishAuthentication();
                AccessTokenPair tokens = mDBApi.getSession().getAccessTokenPair();
                storeDropboxKeys(tokens.key, tokens.secret);
            } catch (IllegalStateException e) {
                Log.i(FlashcardRoidApplication.TAG, "Error authenticating", e);
            }
        }
    }
    
    /**
     * Dropbox アクセスキーを保持しておく
     * @param key
     * @param secret
     */
    private void storeDropboxKeys(String key, String secret) {
        // TODO SharedPreferences に保持しておく実装
    }
    /**
     * Dropbox アクセスキーを取得する
     * @return
     */
    private String[] getStoredDropboxKeys() {
        // TODO SharedPreferences から アクセスキーを取得する実装
        return null;
    }

    /**
     * Dropbox セッションを作成する
     * @return
     */
    private AndroidAuthSession buildDropboxSession() {
        AppKeyPair appKeyPair 
            = new AppKeyPair(FlashcardRoidApplication.DROPBOX_APP_KEY, FlashcardRoidApplication.DROPBOX_APP_SCECRET);

        AndroidAuthSession session  = null;
        
        String[] keys = getStoredDropboxKeys();
        if (keys == null) {
            session = new AndroidAuthSession(appKeyPair, FlashcardRoidApplication.DROPBOX_ACCESS_TYPE);
        } else {
            AccessTokenPair accessToken = new AccessTokenPair(keys[0], keys[1]);
            session = new AndroidAuthSession(appKeyPair, FlashcardRoidApplication.DROPBOX_ACCESS_TYPE, accessToken);
        }
        return session;
    }
}

この状態だと、毎回、以下の認証画面が出てくるので、SharedPreferences などに保存しておく。上記SDKに含まれるサンプルでは、そうしているので参考にするとよい。

ここまでで実行

android_dropbox04

Dropboxの認証アクティビティが表示され、許可すると処理が続行されるようになる。

ファイルをアップロードしてみる

とりあえず、ボタンを作って、ファイルをアップロードしてみる。以下を、アクティビティの onCreate に記述(レイアウトにもボタンを追加)

        Button btnUpload = (Button)findViewById(R.id.btn_upload);
        btnUpload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Date now = new Date();
                    InputStream in = new ByteArrayInputStream(now.toString().getBytes());
                    
                    String filename = String.format("/%tY%tm%td%tH%tM%tS.txt", now, now, now, now, now, now);
                    Entry entry = mDBApi.putFile(filename, in, now.toString().getBytes().length, null, null);

                } catch (Exception e) {
                    Log.e(FlashcardRoidApplication.TAG,"Upload Error", e);
                }
            }
        });        

android_dropbox05

ファイルがアップロード、同期された。PCの方に通知される。

ファイルの一覧を取得し、メタデータを表示してみる

search() の query 引数には、3文字以上渡す必要があるようだ。"txt" を渡しているが、"*.*" のようなワイルドカードはどうも機能しない感じ。

        
        Button btnSearch = (Button)findViewById(R.id.btn_search);
        btnSearch.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View v) {
                try {
                    int filelimit = -1; // Default is 10,000 if you pass in 0 or less
                    List entries = mDBApi.search("/", "txt", filelimit, false); 
                    for (Entry entry : entries) {
                        // メタデータを含めて表示
                        Log.i(FlashcardRoidApplication.TAG, 
                                String.format("path=%s,rev=%s", 
                                        entry.path,
                                        entry.rev));
                    }
                } catch (Exception e) {
                    Log.e(FlashcardRoidApplication.TAG,"Upload Error", e);
                }
            }
        });

上記でアップロードしたファイル情報が取得できている

android_dropbox06

ファイルをダウンロードしてみる

上記で作成して、Dropbox 上に保存したファイル(最新バージョン)を、Andoroid 端末の SDカード上にダウンロードしてみる。

        Button btnDown = (Button)findViewById(R.id.btn_download);
        btnDown.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View v) {
                try {
                    
                    String target = "20120525171459.txt"; // 対象ファイル名
                    
                    // 端末の SDカード上のデータ格納先
                    File sdcarddir = Environment.getExternalStorageDirectory();
                    String apppath = sdcarddir.getAbsolutePath() 
                            + File.separatorChar 
                            + "data"
                            + File.separatorChar 
                            + this.getClass().getPackage().getName().replaceAll("[.]", Character.toString(File.separatorChar))
                            + File.separatorChar
                            ;
                    File appDir = new File(apppath);
                    if (!appDir.exists()) { 
                        if(!appDir.mkdirs()) {
                            throw new IllegalStateException("おそらく、android.permission.WRITE_EXTERNAL_STORAGE が マニフェストに設定されてないのでは?");
                        }
                    }
                    
                    File file = new File(apppath + File.separatorChar + target);
                    
                    Log.i(FlashcardRoidApplication.TAG, "download destination : " + file.getAbsolutePath());
                    
                    OutputStream os = new FileOutputStream(file);
                    DropboxFileInfo dfi = mDBApi.getFile(File.separatorChar + target, null, os, null);
                    
                    Log.i(FlashcardRoidApplication.TAG, "file's revision : " + dfi.getMetadata().rev);
                    
                    
                } catch(Exception e) {
                    Log.e(FlashcardRoidApplication.TAG,"Download Error", e);
                }
            }
        });

Dropbox では、変更を 30日まで保持している(Pack-Rat アドオン利用時にはそれ以上)が、DropboxFileInfo は、メタデータエントリーを含んでおり、rev を比較することで、最新版かどうかを判定できる。

android_dropbox07

端末のSDカード、アプリケーションフォルダにダウンロードされた。

うーん簡単に使えて便利。アプリ開発の幅が拡がりそうな気はする。

あと、Revert とか、Conflict とか、通常のバージョン管理システムで意識するような点は

どうするのが Dropbox 的に良いのかが気になるが、単純に使う分には問題なさそうだ。