Pocket (Formerly Read It Later) のOAuthログインサンプルを作りました
全てのソースコードは下記より
https://github.com/noboru-i/ReadItNow/tree/pocket-login-sample
下記のように取得できると思います。
git clone https://github.com/noboru-i/ReadItNow.git cd ReadItNow git checkout -b pocket-login-sample refs/tags/pocket-login-sample
取得後、
/res/values/common_strings.xml
を
<string name="pocket_api_key" translatable="false">10633-60f45438c2b0e5f6a98387fe</string>
のようにAPIキーを設定します。
基本的に、
http://getpocket.com/developer/docs/authentication
を実装しただけです。
APIキーの取得
http://getpocket.com/developer/
より、「CREATE NEW APP」を押下。
- Application Name
アプリケーション名を指定します。
http://getpocket.com/developer/docs/branding にあるように、「Pocket」・「Read It Later」は利用できないようです。
- Application Description
説明文を指定します。
認証画面で表示されます。
- Permissions
参照だけであれば、「Retrieve」だけでよいと思います。
- Platforms
Androidアプリであれば、「Android - Mobile」だけでよいと思います。
上記を入力し、「CREATE APPLICATION」をクリックすると、CONSUMER KEYが表示されます。
更新時には下記を追加で指定できます。
- URL
play storeのURLなどでよいかと思います。紹介ページがあればそちらを。
- Application Icon
アイコンを指定します。
認証画面などで表示されます。
- Categories
カテゴリを指定します。
http://getpocket.com/apps/
に表示される際に利用されるようです。
認証を解除する
ブラウザより
http://getpocket.com/connected_accounts
にアクセスし、「Remove access」をタップ
処理フロー
まず、MainActivity#onCreateが呼び出されます。
ランチャーから起動された場合、intent.getAction()には"android.intent.action.MAIN"が入っていることになります。
なので、36行目のif文の中には入らずに72行目のtaskを起動します。
getRequestTokenで、request tokenを取得し、preferenceに保存します。
startOauthActivityで、外部ブラウザを起動し、認証してもらいます。
認証から戻ってきた場合、36行目のif文に入ります。
preferenceよりrequest tokenを取得し、43行目のtaskを起動します。
getAccessTokenで、access tokenを取得する。
/src/hm/orz/chaos114/android/readitnow/MainActivity.java
https://github.com/noboru-i/ReadItNow/blob/pocket-login-sample/src/hm/orz/chaos114/android/readitnow/MainActivity.java
package hm.orz.chaos114.android.readitnow; import java.io.IOException; import java.io.UnsupportedEncodingException; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.json.JSONObject; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.Log; import android.view.Menu; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = getIntent(); if (Intent.ACTION_VIEW.equals(intent.getAction())) { Uri data = intent.getData(); if (data != null && "authorizationFinished".equals(data.getEncodedAuthority())) { SharedPreferences preferences = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); final String requestToken = preferences.getString( "REQUEST_TOKEN", null); AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { @Override protected Boolean doInBackground(Void... params) { try { getAccessToken(requestToken); } catch (Exception e) { return false; } return true; } @Override protected void onPostExecute(Boolean result) { if (!result) { // 認証失敗 Toast.makeText(MainActivity.this, "認証に失敗しました", Toast.LENGTH_LONG).show(); return; } SharedPreferences preferences = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); String username = preferences.getString("USERNAME", null); TextView view = (TextView)MainActivity.this.findViewById(R.id.main_test); view.setText("Hello " + username); } }; task.execute((Void) null); return; } } AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { // request tokenを取得する String requestToken = getRequestToken(); SharedPreferences preferences = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); Editor editor = preferences.edit(); editor.putString("REQUEST_TOKEN", requestToken); editor.commit(); // 認証画面を表示する startOauthActivity(requestToken); return null; } }; task.execute((Void) null); } private String getRequestToken() { String url = getString(R.string.url_v3_request); JSONObject param = new JSONObject(); try { param.put("consumer_key", getString(R.string.pocket_api_key)); param.put("redirect_uri", "readitnow://authorizationFinished"); String response = postJson(url, param); JSONObject respJson = new JSONObject(response); String requestToken = respJson.getString("code"); return requestToken; } catch (Exception e) { throw new RuntimeException(e); } } private void startOauthActivity(String requestToken) { String url = getString(R.string.url_authrize); Uri.Builder builder = new Uri.Builder(); builder.appendQueryParameter("request_token", requestToken); builder.appendQueryParameter("redirect_uri", "readitnow://authorizationFinished"); String queryString = builder.build().toString(); url = getString(R.string.url_authrize); Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(Uri.parse(url + queryString)); startActivity(intent); } private void getAccessToken(String requestToken) { String url = getString(R.string.url_v3_authrize); JSONObject param = new JSONObject(); try { param.put("consumer_key", getString(R.string.pocket_api_key)); param.put("code", requestToken); String response = postJson(url, param); JSONObject respJson = new JSONObject(response); String accessToken = respJson.getString("access_token"); String username = respJson.getString("username"); SharedPreferences preferences = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); Editor editor = preferences.edit(); editor.putString("USERNAME", username); editor.putString("ACCESS_TOKEN", accessToken); editor.commit(); Log.d(TAG, "accessToken = " + accessToken); Log.d(TAG, "username = " + username); } catch (Exception e) { throw new RuntimeException(e); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } /** * APサーバにPOSTリクエストを発行する。 * * @param endpoint リクエストURL * @param params リクエストパラメータ * @return レスポンス文字列 * @throws IOException 通信例外 */ private static String postJson(final String endpoint, final JSONObject params) throws IOException { Log.i(TAG, "endpoint = " + endpoint); DefaultHttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(endpoint); try { httpPost.setHeader(HTTP.CONTENT_TYPE, "application/json"); httpPost.setHeader("X-Accept", "application/json"); StringEntity se = new StringEntity(params.toString()); httpPost.setEntity(se); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } HttpResponse response = httpClient.execute(httpPost); String responseString = EntityUtils.toString(response.getEntity()); Log.i(TAG, "response = " + responseString); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200) { throw new IOException("Post failed. statusCode=" + statusCode); } return responseString; } }
/res/values/url_strings.xml
https://github.com/noboru-i/ReadItNow/blob/pocket-login-sample/res/values/url_strings.xml
<resources> <string name="url_v3_request" translatable="false">https://getpocket.com/v3/oauth/request</string> <string name="url_authrize" translatable="false">https://getpocket.com/auth/authorize</string> <string name="url_v3_authrize" translatable="false">https://getpocket.com/v3/oauth/authorize</string> </resources>