SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/api/TootApiClient.java

239 lines
8.2 KiB
Java
Raw Normal View History

2017-04-20 18:23:59 +02:00
package jp.juggler.subwaytooter.api;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
2017-04-20 18:23:59 +02:00
import java.util.UUID;
import jp.juggler.subwaytooter.App1;
2017-04-20 18:23:59 +02:00
import jp.juggler.subwaytooter.table.SavedAccount;
import jp.juggler.subwaytooter.util.CancelChecker;
import jp.juggler.subwaytooter.util.LogCategory;
import jp.juggler.subwaytooter.R;
import jp.juggler.subwaytooter.util.Utils;
import jp.juggler.subwaytooter.table.ClientInfo;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
2017-04-20 18:23:59 +02:00
public class TootApiClient {
private static final LogCategory log = new LogCategory( "TootApiClient" );
static final OkHttpClient ok_http_client = App1.ok_http_client;
2017-04-20 18:23:59 +02:00
public interface Callback {
boolean isApiCancelled();
2017-04-20 18:23:59 +02:00
void publishApiProgress( String s );
2017-04-20 18:23:59 +02:00
}
private final Context context;
public final Callback callback;
public TootApiClient( @NonNull Context context, @NonNull Callback callback ){
this.context = context;
this.callback = callback;
}
private String instance;
private String user_mail;
private String password;
private SavedAccount account;
2017-04-20 18:23:59 +02:00
public void setUserInfo( String instance, String user_mail, String password ){
this.instance = instance;
this.user_mail = user_mail;
this.password = password;
}
public void setAccount( SavedAccount account ){
this.instance = account.host;
this.account = account;
2017-04-20 18:23:59 +02:00
}
public static final MediaType MEDIA_TYPE_FORM_URL_ENCODED = MediaType.parse( "application/x-www-form-urlencoded" );
public TootApiResult request( String path ){
return request(path,new Request.Builder() );
}
public TootApiResult request( String path, Request.Builder request_builder ){
2017-04-20 18:23:59 +02:00
JSONObject client_info = null;
JSONObject token_info = ( account == null ? null : account.token_info );
2017-04-20 18:23:59 +02:00
for( ; ; ){
if( callback.isApiCancelled() ) return null;
if( token_info == null ){
if( client_info == null ){
// DBにあるならそれを使う
client_info = ClientInfo.load( instance );
if( client_info != null ) continue;
callback.publishApiProgress( context.getString( R.string.register_app_to_server, instance ) );
// OAuth2 クライアント登録
String client_name = "jp.juggler.subwaytooter." + UUID.randomUUID().toString();
Request request = new Request.Builder()
.url( "https://" + instance + "/api/v1/apps" )
.post( RequestBody.create( MEDIA_TYPE_FORM_URL_ENCODED
, "client_name=" + Uri.encode( client_name )
+ "&redirect_uris=urn:ietf:wg:oauth:2.0:oob"
+ "&scopes=read write follow"
) )
.build();
Call call = ok_http_client.newCall( request );
Response response;
try{
response = call.execute();
}catch( Throwable ex ){
return new TootApiResult( Utils.formatError( ex, context.getResources(), R.string.network_error ) );
}
if( callback.isApiCancelled() ) return null;
if( ! response.isSuccessful() ){
return new TootApiResult( context.getString( R.string.network_error_arg, response ) );
}
try{
String json = response.body().string();
if( TextUtils.isEmpty( json ) || json.startsWith( "<" ) ){
return new TootApiResult( context.getString( R.string.response_not_json ) + "\n" + json );
}
// {"id":999,"redirect_uri":"urn:ietf:wg:oauth:2.0:oob","client_id":"******","client_secret":"******"}
client_info = new JSONObject( json );
String error = Utils.optStringX( client_info, "error" );
if( ! TextUtils.isEmpty( error ) ){
return new TootApiResult( context.getString( R.string.api_error, error ) );
}
ClientInfo.save( instance, json );
continue;
}catch( Throwable ex ){
ex.printStackTrace();
return new TootApiResult( Utils.formatError( ex, "API data error" ) );
2017-04-20 18:23:59 +02:00
}
}
if( password == null ){
// 手動でアクセストークンを再取得しなければいけない
return new TootApiResult( context.getString( R.string.login_required ) );
}
callback.publishApiProgress( context.getString( R.string.request_access_token ) );
2017-04-20 18:23:59 +02:00
// アクセストークンの取得
Request request = new Request.Builder()
.url( "https://" + instance + "/oauth/token" )
.post( RequestBody.create(
MEDIA_TYPE_FORM_URL_ENCODED
,"client_id=" + Uri.encode( Utils.optStringX( client_info, "client_id" ) )
+ "&client_secret=" + Uri.encode( Utils.optStringX( client_info, "client_secret" ) )
+ "&grant_type=password"
+ "&username=" + Uri.encode( user_mail )
+ "&password=" + Uri.encode( password )
+ "&scope=read write follow"
+ "&scopes=read write follow"
))
.build();
Call call = ok_http_client.newCall( request );
2017-04-20 18:23:59 +02:00
Response response;
try{
response = call.execute();
}catch( Throwable ex ){
return new TootApiResult( Utils.formatError( ex, context.getResources(), R.string.network_error ) );
2017-04-20 18:23:59 +02:00
}
if( callback.isApiCancelled() ) return null;
// TODO: アプリIDが無効な場合はどんなエラーが出る
if( ! response.isSuccessful() ){
return new TootApiResult( context.getString( R.string.network_error_arg, response ) );
}
2017-04-20 18:23:59 +02:00
try{
String json = response.body().string();
2017-04-20 18:23:59 +02:00
// {"access_token":"******","token_type":"bearer","scope":"read","created_at":1492334641}
if( TextUtils.isEmpty( json ) || json.charAt( 0 ) == '<' ){
return new TootApiResult( context.getString( R.string.login_failed ) );
}
token_info = new JSONObject( json );
2017-04-20 18:23:59 +02:00
String error = Utils.optStringX( client_info, "error" );
if( ! TextUtils.isEmpty( error ) ){
return new TootApiResult( context.getString( R.string.api_error, error ) );
}
if( account != null ){
account.updateTokenInfo( token_info );
}
2017-04-20 18:23:59 +02:00
continue;
}catch( Throwable ex ){
2017-04-20 18:23:59 +02:00
ex.printStackTrace();
return new TootApiResult( Utils.formatError( ex, "API data error" ) );
}
}
// アクセストークンを使ってAPIを呼び出す
{
callback.publishApiProgress( context.getString( R.string.request_api, path ) );
Request request = request_builder
.url("https://" + instance + path)
.header( "Authorization", "Bearer " + Utils.optStringX( token_info, "access_token" ) )
.build();
Call call = ok_http_client.newCall( request );
Response response;
try{
response = call.execute();
}catch( Throwable ex ){
return new TootApiResult( Utils.formatError( ex, context.getResources(), R.string.network_error ) );
}
if( callback.isApiCancelled() ) return null;
// TODO: アクセストークンが無効な場合はどうなる?
// TODO: アプリIDが無効な場合はどうなる
if( ! response.isSuccessful() ){
return new TootApiResult( context.getString( R.string.network_error_arg, response ) );
}
try{
String json = response.body().string();
if( TextUtils.isEmpty( json ) || json.startsWith( "<" ) ){
return new TootApiResult( context.getString( R.string.response_not_json ) + "\n" + json );
}else if( json.startsWith( "[" ) ){
JSONArray array = new JSONArray( json );
return new TootApiResult( token_info, json, array );
}else{
JSONObject object = new JSONObject( json );
String error = Utils.optStringX( object, "error" );
if( ! TextUtils.isEmpty( error ) ){
return new TootApiResult( context.getString( R.string.api_error, error ) );
}
return new TootApiResult( token_info, json, object );
2017-04-20 18:23:59 +02:00
}
}catch( Throwable ex ){
ex.printStackTrace();
return new TootApiResult( Utils.formatError( ex, "API data error" ) );
2017-04-20 18:23:59 +02:00
}
}
}
}
}