AndroidでOAuth2を使ってDropboxの認証をする


まぁタイトルの通りですが、とりあえず認証済みかを確認して認証していない場合は認証ページを出して認証後AccessTokenを保存し、それを元にDropboxAPIのインスタンスを作る所までまとめておきます。

公式のドキュメントにも[チュートリアル](https://www.dropbox.com/developers/core/start/android)があるのでこっちを参考にしてもどうぞ。


#簡潔なアジェンダ
1.DropboxAPIを使用する為に必要なAppの登録方法
2.DropboxSDKのダウンロードAndroidStudioでの組み込み方法
3.AndroidManifest.xmlの編集
4.Activityにコードを書いていく

#Appを作成
[ここ](https://www.dropbox.com/developers/apps)にアクセスすると右側に「Create app」があるのでクリックします。
すると、次のような質問をされるので「Dropbox API app」を選択します。
![スクリーンショット 2015-03-24 18.03.15.png](https://qiita-image-store.s3.amazonaws.com/0/55941/bc7f4312-e4fa-7f5b-5f99-5993997748c7.png)
選択すると、「ファイルとデータストアを使用するのか」といった質問を受けるのでそれぞれ答えていきます。
最後にアプリケーションの名前を入力するよう促されるので入力しておしまいです。

#DropboxからSDKを取ってくる
[ここ](https://www.dropbox.com/developers/core/sdks/android)からSDKを落とします。
![スクリーンショット 2015-03-24 17.47.48.png](https://qiita-image-store.s3.amazonaws.com/0/55941/7279d114-bfa0-dd90-cd82-18a92566bac0.png)

#SDKをAndroidStudioのプロジェクトに組み込む
落としたSDKを展開すると、libの中にjarファイルが2つあります。
![スクリーンショット 2015-03-24 17.49.27.png](https://qiita-image-store.s3.amazonaws.com/0/55941/cef89bbc-6d05-0d9e-6333-e870c0bb62db.png)
このファイルをプロジェクト内にあるlibsに放り込みます。
![スクリーンショット 2015-03-24 17.54.33.png](https://qiita-image-store.s3.amazonaws.com/0/55941/4b909479-1103-e1ff-f9b4-1981c1bc99a2.png)
そして、AndroidStudioの上部にある次のボタンを押して読み込ませます。
![スクリーンショット 2015-03-24 17.56.48.png](https://qiita-image-store.s3.amazonaws.com/0/55941/2515e474-82e8-d00d-246a-c670fc59f836.png)

#AndroidManifest.xmlの編集
認証には **com.dropbox.client2.android.AuthActivity** というActivityを使用するため、AndroidManifestに記述します。
この時、dataのandroid:schemeにAppKeyを入力する必要があります。注意する点として、db-の後ろに追記する必要があることです。
また、uses-permissionでINTERNETを許可するようにもしておきます。

```
		<activity
            android:name="com.dropbox.client2.android.AuthActivity"
            android:configChanges="orientation|keyboard"
            android:launchMode="singleTask">
            <intent-filter>

                <data android:scheme="db-(AppKey)" />

                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
```

全体的にはこのような内容になります。

```xml:AndroidManifest.xml
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.dropbox.client2.android.AuthActivity"
            android:configChanges="orientation|keyboard"
            android:launchMode="singleTask">
            <intent-filter>

                <data android:scheme="db-57b86cj98uo2nwl" />

                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>

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

#Activityにコードを記述
認証情報を保持するためにSharedPreferencesを用いますが、まずはそれを管理するクラスを作成します。

```java:DropboxUtils.java
public class DropboxUtils {
    private static final String TOKEN = "token";
    private static final String PREF_NAME = "dropbox";
    public static final String APPKEY = "appkey";
    public static final String APPKEYSECRET = "appsecret";
    private Context context;

    public DropboxUtils(Context context) {
        this.context = context;
    }
    
    public void storeOauth2AccessToken(String secret){
        SharedPreferences preferences = context.getSharedPreferences(PREF_NAME,
                Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putString(TOKEN, secret);
        editor.commit();
    }

    public AndroidAuthSession loadAndroidAuthSession() {
        SharedPreferences preferences = context.getSharedPreferences(PREF_NAME,
                Context.MODE_PRIVATE);
        String token = preferences.getString(TOKEN, null);
        if (token != null) {
            AppKeyPair appKeys = new AppKeyPair(APPKEY, APPKEYSECRET);
            return new AndroidAuthSession(appKeys,token);
        } else {

            return null;
        }
    }

    public boolean hasLoadAndroidAuthSession() {
        return loadAndroidAuthSession() != null;
    }

}
```

そして、MainActivityに認証処理を記述します。
onCreate()で先ほど作成したDropboxUtilsのインスタンスを作成し、認証済みであるのかをチェックします。
未認証であれば認証ページ、認証済みであれば認証情報を取得してDropboxAPIのインスタンスを作成します。

```
@Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dropboxUtils = new DropboxUtils(this);

        //認証済みかを確認
        if(!dropboxUtils.hasLoadAndroidAuthSession()){
            AppKeyPair appKeys = new AppKeyPair(DropboxUtils.APPKEY, DropboxUtils.APPKEYSECRET);
            AndroidAuthSession session = new AndroidAuthSession(appKeys);
            mDBApi = new DropboxAPI<>(session);
            mDBApi.getSession().startOAuth2Authentication(this); //認証していない場合は認証ページを表示
        }else {
            mDBApi = new DropboxAPI<>(dropboxUtils.loadAndroidAuthSession()); //SharedPreferencesから認証情報取得
        }
    }
```

認証ページで認証が終わると、onResume()でAccessTokenの取得を行い保存します。

```
@Override
    protected void onResume() {
        super.onResume();
        if (mDBApi.getSession().authenticationSuccessful()) {
            try {
				mDBApi.getSession().finishAuthentication();
                dropboxUtils.storeOauth2AccessToken(mDBApi.getSession().getOAuth2AccessToken());
                Toast.makeText(this,"認証しました",Toast.LENGTH_SHORT).show();
            } catch (IllegalStateException e) {

            }
        }
    }
```

全体的なコードはこのようになります。

``` java:MainActivity.java
public class MainActivity extends ActionBarActivity {

    private DropboxAPI<AndroidAuthSession> mDBApi;
    private DropboxUtils dropboxUtils;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dropboxUtils = new DropboxUtils(this);

        //認証済みかを確認
        if(!dropboxUtils.hasLoadAndroidAuthSession()){
            AppKeyPair appKeys = new AppKeyPair(DropboxUtils.APPKEY, DropboxUtils.APPKEYSECRET);
            AndroidAuthSession session = new AndroidAuthSession(appKeys);
            mDBApi = new DropboxAPI<>(session);
            mDBApi.getSession().startOAuth2Authentication(this); //認証していない場合は認証ページを表示
        }else {
            mDBApi = new DropboxAPI<>(dropboxUtils.loadAndroidAuthSession()); //SharedPreferencesから認証情報取得
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mDBApi.getSession().authenticationSuccessful()) {
            try {
                // Required to complete auth, sets the access token on the session
                mDBApi.getSession().finishAuthentication();
                dropboxUtils.storeOauth2AccessToken(mDBApi.getSession().getOAuth2AccessToken());
                Toast.makeText(this,"認証しました",Toast.LENGTH_SHORT).show();
            } catch (IllegalStateException e) {

            }
        }
    }
}
```

これで実装は終了です。

#実際の動作
実際に動作を確認してみます。
まず、認証をしていないため認証ページへ自動的に遷移します。
![スクリーンショット 2015-03-24 19.02.40.png](https://qiita-image-store.s3.amazonaws.com/0/55941/ddf83a94-e907-ff38-65f5-0a7d3fff7beb.png)
リクエストの許可をすると、MainActivityへ戻ります。
![スクリーンショット 2015-03-24 19.03.21.png](https://qiita-image-store.s3.amazonaws.com/0/55941/2db50fbd-eb6f-151d-35d3-a5e6c962ac1c.png)
再度、アプリを立ち上げると認証済みであるため認証ページへは遷移せずそのままMainActivityが動きます。
![スクリーンショット 2015-03-24 18.59.55.png](https://qiita-image-store.s3.amazonaws.com/0/55941/b905206b-c173-8137-4f46-7ec0dec3347e.png "スクリーンショット 2015-03-24 18.59.55.png")