2018-01-04 19:52:25 +01:00
|
|
|
|
package jp.juggler.subwaytooter.api
|
|
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
|
import android.net.Uri
|
|
|
|
|
|
|
|
|
|
import org.json.JSONException
|
|
|
|
|
import org.json.JSONObject
|
|
|
|
|
|
|
|
|
|
import jp.juggler.subwaytooter.App1
|
|
|
|
|
import jp.juggler.subwaytooter.table.ClientInfo
|
|
|
|
|
import jp.juggler.subwaytooter.table.SavedAccount
|
|
|
|
|
import jp.juggler.subwaytooter.util.LogCategory
|
|
|
|
|
import jp.juggler.subwaytooter.R
|
|
|
|
|
import jp.juggler.subwaytooter.util.Utils
|
|
|
|
|
import okhttp3.Call
|
|
|
|
|
import okhttp3.MediaType
|
|
|
|
|
import okhttp3.Request
|
|
|
|
|
import okhttp3.RequestBody
|
|
|
|
|
import okhttp3.Response
|
|
|
|
|
import okhttp3.WebSocketListener
|
|
|
|
|
import org.json.JSONArray
|
|
|
|
|
|
|
|
|
|
class TootApiClient(
|
|
|
|
|
private val context : Context,
|
|
|
|
|
private val callback : TootApiCallback
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
private val log = LogCategory("TootApiClient")
|
|
|
|
|
|
|
|
|
|
private val ok_http_client = App1.ok_http_client
|
|
|
|
|
|
|
|
|
|
val MEDIA_TYPE_FORM_URL_ENCODED = MediaType.parse("application/x-www-form-urlencoded")
|
|
|
|
|
val MEDIA_TYPE_JSON = MediaType.parse("application/json;charset=UTF-8")
|
|
|
|
|
|
|
|
|
|
private const val DEFAULT_CLIENT_NAME = "SubwayTooter"
|
|
|
|
|
private const val KEY_CLIENT_CREDENTIAL = "SubwayTooterClientCredential"
|
|
|
|
|
|
|
|
|
|
private const val KEY_AUTH_VERSION = "SubwayTooterAuthVersion"
|
|
|
|
|
private const val AUTH_VERSION = 1
|
|
|
|
|
private const val REDIRECT_URL = "subwaytooter://oauth/"
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface CurrentCallCallback {
|
|
|
|
|
fun onCallCreated(call : Call)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var call_callback : CurrentCallCallback? = null
|
|
|
|
|
|
|
|
|
|
// インスタンスのホスト名
|
|
|
|
|
var instance : String? = null
|
|
|
|
|
|
|
|
|
|
// アカウントがある場合に使用する
|
|
|
|
|
var account : SavedAccount? = null
|
|
|
|
|
|
|
|
|
|
@Suppress("unused")
|
|
|
|
|
val isApiCancelled : Boolean
|
|
|
|
|
get() = callback.isApiCancelled
|
|
|
|
|
|
|
|
|
|
val isCancelled : Boolean
|
|
|
|
|
get() = callback.isApiCancelled
|
|
|
|
|
|
|
|
|
|
fun publishApiProgress(s : String) {
|
|
|
|
|
callback.publishApiProgress(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun publishApiProgressRatio(value : Int, max : Int) {
|
|
|
|
|
callback.publishApiProgressRatio(value, max)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun setCurrentCallCallback(call_callback : CurrentCallCallback) {
|
|
|
|
|
this.call_callback = call_callback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// アカウント追加時に使用する
|
|
|
|
|
fun setInstance(instance : String?) : TootApiClient {
|
|
|
|
|
this.instance = instance
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun setAccount(account : SavedAccount) : TootApiClient {
|
|
|
|
|
this.account = account
|
|
|
|
|
this.instance = account.host
|
|
|
|
|
return this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@JvmOverloads
|
|
|
|
|
fun request(path : String, request_builder : Request.Builder = Request.Builder()) : TootApiResult? {
|
|
|
|
|
log.d("request: $path")
|
|
|
|
|
val result = request_sub(path, request_builder)
|
|
|
|
|
val error = result?.error
|
|
|
|
|
if(error != null) log.d("error: $error")
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun request_sub(path : String, request_builder : Request.Builder) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(instance)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(result.error != null) return result
|
|
|
|
|
val instance = result.caption // same to instance
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val account = this.account ?: return result.setError("account is null")
|
|
|
|
|
val access_token = account.getAccessToken()
|
|
|
|
|
|
|
|
|
|
val response = try {
|
|
|
|
|
request_builder.url("https://" + instance + path)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(access_token != null && access_token.isNotEmpty()) {
|
|
|
|
|
request_builder.header("Authorization", "Bearer " + access_token)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendRequest(request_builder.build())
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError(instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
return readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun webSocket(path : String, request_builder : Request.Builder, ws_listener : WebSocketListener) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(instance)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(result.error != null) return result
|
|
|
|
|
val instance = result.caption // same to instance
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
val account = this.account ?: return TootApiResult("account is null")
|
|
|
|
|
val access_token = account.getAccessToken()
|
|
|
|
|
|
|
|
|
|
var url = "wss://" + instance + path
|
|
|
|
|
|
|
|
|
|
if(access_token != null && access_token.isNotEmpty()) {
|
|
|
|
|
val delm = if(- 1 != url.indexOf('?')) '&' else '?'
|
|
|
|
|
url = url + delm + "access_token=" + Uri.encode(access_token)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
request_builder.url(url)
|
|
|
|
|
val request = request_builder.build()
|
|
|
|
|
callback.publishApiProgress(context.getString(R.string.request_api, request.method(), path))
|
|
|
|
|
val ws = ok_http_client.newWebSocket(request, ws_listener)
|
|
|
|
|
if(callback.isApiCancelled) {
|
|
|
|
|
ws.cancel()
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
result.data = ws
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
result.error = instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error)
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 疑似アカウントの追加時に、インスタンスの検証を行う
|
|
|
|
|
fun checkInstance() : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(instance)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(result.error != null) return result
|
|
|
|
|
val instance = result.caption // same to instance
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
val response = try {
|
|
|
|
|
val request = Request.Builder().url("https://$instance/api/v1/instance").build()
|
|
|
|
|
sendRequest(request)
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
return result.setError(instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
return readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// クライアントアプリの登録を確認するためのトークンを生成する
|
|
|
|
|
// oAuth2 Client Credentials の取得
|
|
|
|
|
// https://github.com/doorkeeper-gem/doorkeeper/wiki/Client-Credentials-flow
|
|
|
|
|
// このトークンはAPIを呼び出すたびに新しく生成される…
|
|
|
|
|
private fun getClientCredential(client_info : JSONObject) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(this.instance)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(result.error != null) return result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val instance = result.caption
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val response = try {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
val request = Request.Builder()
|
|
|
|
|
.url("https://$instance/oauth/token")
|
|
|
|
|
.post(RequestBody.create(MEDIA_TYPE_FORM_URL_ENCODED, "grant_type=client_credentials"
|
|
|
|
|
+ "&client_id=" + Uri.encode(client_info.optString("client_id"))
|
|
|
|
|
+ "&client_secret=" + Uri.encode(client_info.optString("client_secret"))
|
|
|
|
|
))
|
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
sendRequest(request)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError("getClientCredential: " + instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
|
|
|
|
val r2 = readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonObject = r2?.jsonObject ?: return r2
|
|
|
|
|
val sv = Utils.optStringX(jsonObject, "access_token")
|
|
|
|
|
if(sv?.isNotEmpty() == true) {
|
|
|
|
|
result.data = sv
|
|
|
|
|
} else {
|
|
|
|
|
result.data = null
|
|
|
|
|
result.error = "getClientCredential: API returns empty client_credential."
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// client_credentialがまだ有効か調べる
|
|
|
|
|
private fun verifyClientCredential(client_credential : String) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(this.instance)
|
|
|
|
|
if(result.error != null) return result
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val instance = result.caption // same to instance
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
val response = try {
|
|
|
|
|
val request = Request.Builder()
|
|
|
|
|
.url("https://$instance/api/v1/apps/verify_credentials")
|
|
|
|
|
.header("Authorization", "Bearer $client_credential")
|
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
sendRequest(request)
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError("$instance: " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
return readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun prepareBrowserUrl(client_info : JSONObject) : String {
|
|
|
|
|
val account = this.account
|
|
|
|
|
|
|
|
|
|
// 認証ページURLを作る
|
|
|
|
|
val browser_url = ("https://" + instance + "/oauth/authorize"
|
|
|
|
|
+ "?client_id=" + Uri.encode(Utils.optStringX(client_info, "client_id"))
|
|
|
|
|
+ "&response_type=code"
|
|
|
|
|
+ "&redirect_uri=" + Uri.encode(REDIRECT_URL)
|
|
|
|
|
+ "&scope=read write follow"
|
|
|
|
|
+ "&scopes=read write follow"
|
|
|
|
|
+ "&state=" + (if(account != null) "db:" + account.db_id else "host:" + instance)
|
|
|
|
|
+ "&grant_type=authorization_code"
|
|
|
|
|
+ "&approval_prompt=force"
|
|
|
|
|
// +"&access_type=offline"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return browser_url
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// クライアントを登録してブラウザで開くURLを生成する
|
|
|
|
|
fun authorize1(clientNameArg : String) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(this.instance)
|
|
|
|
|
if(result.error != null) return result
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val instance = result.caption // same to instance
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// クライアントIDがアプリ上に保存されているか?
|
|
|
|
|
val client_name = if(clientNameArg.isNotEmpty()) clientNameArg else DEFAULT_CLIENT_NAME
|
|
|
|
|
val client_info = ClientInfo.load(instance, client_name)
|
|
|
|
|
if(client_info != null) {
|
|
|
|
|
|
|
|
|
|
var client_credential = Utils.optStringX(client_info, KEY_CLIENT_CREDENTIAL)
|
|
|
|
|
|
|
|
|
|
// client_credential をまだ取得していないなら取得する
|
|
|
|
|
if(client_credential == null || client_credential.isEmpty()) {
|
|
|
|
|
val resultSub = getClientCredential(client_info)
|
|
|
|
|
client_credential = resultSub?.string
|
|
|
|
|
if(client_credential?.isNotEmpty() == true) {
|
|
|
|
|
try {
|
|
|
|
|
client_info.put(KEY_CLIENT_CREDENTIAL, client_credential)
|
|
|
|
|
ClientInfo.save(instance, client_name, client_info.toString())
|
|
|
|
|
} catch(ignored : JSONException) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// client_credential があるならcredentialがまだ使えるか確認する
|
|
|
|
|
if(client_credential?.isNotEmpty() == true) {
|
|
|
|
|
val resultSub = verifyClientCredential(client_credential)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(resultSub?.jsonObject != null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
result.data = prepareBrowserUrl(client_info)
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// OAuth2 クライアント登録
|
|
|
|
|
val response = try {
|
|
|
|
|
val request = Request.Builder()
|
|
|
|
|
.url("https://$instance/api/v1/apps")
|
|
|
|
|
.post(RequestBody.create(MEDIA_TYPE_FORM_URL_ENCODED, "client_name=" + Uri.encode(client_name)
|
|
|
|
|
+ "&redirect_uris=" + Uri.encode(REDIRECT_URL)
|
|
|
|
|
+ "&scopes=read write follow"
|
|
|
|
|
))
|
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
sendRequest(request)
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError(instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val r2 = readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonObject = r2?.jsonObject ?: return r2
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// {"id":999,"redirect_uri":"urn:ietf:wg:oauth:2.0:oob","client_id":"******","client_secret":"******"}
|
|
|
|
|
jsonObject.put(KEY_AUTH_VERSION, AUTH_VERSION)
|
|
|
|
|
ClientInfo.save(instance, client_name, jsonObject.toString())
|
2018-01-10 16:47:35 +01:00
|
|
|
|
result.data = prepareBrowserUrl(jsonObject)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// oAuth2認証の続きを行う
|
|
|
|
|
fun authorize2(clientNameArg : String, code : String) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(instance)
|
|
|
|
|
if(result.error != null) return result
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val instance = result.caption // same to instance
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val client_name = if(clientNameArg.isNotEmpty()) clientNameArg else DEFAULT_CLIENT_NAME
|
|
|
|
|
val client_info = ClientInfo.load(instance, client_name) ?: return result.setError("missing client id")
|
|
|
|
|
|
|
|
|
|
var response = try {
|
|
|
|
|
|
|
|
|
|
val post_content = ("grant_type=authorization_code"
|
|
|
|
|
+ "&code=" + Uri.encode(code)
|
|
|
|
|
+ "&client_id=" + Uri.encode(Utils.optStringX(client_info, "client_id"))
|
|
|
|
|
+ "&redirect_uri=" + Uri.encode(REDIRECT_URL)
|
|
|
|
|
+ "&client_secret=" + Uri.encode(Utils.optStringX(client_info, "client_secret"))
|
|
|
|
|
+ "&scope=read write follow"
|
|
|
|
|
+ "&scopes=read write follow")
|
|
|
|
|
|
|
|
|
|
val request = Request.Builder()
|
|
|
|
|
.url("https://$instance/oauth/token")
|
|
|
|
|
.post(RequestBody.create(MEDIA_TYPE_FORM_URL_ENCODED, post_content))
|
|
|
|
|
.build()
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
sendRequest(request)
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError(instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val token_info : JSONObject
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val r2 = readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonObject = r2?.jsonObject ?: return r2
|
|
|
|
|
|
|
|
|
|
// {"access_token":"******","token_type":"bearer","scope":"read","created_at":1492334641}
|
|
|
|
|
jsonObject.put(KEY_AUTH_VERSION, AUTH_VERSION)
|
|
|
|
|
token_info = jsonObject
|
|
|
|
|
result.token_info = jsonObject
|
|
|
|
|
|
|
|
|
|
val access_token = Utils.optStringX(token_info, "access_token")
|
|
|
|
|
if(access_token == null || access_token.isEmpty()) {
|
|
|
|
|
return result.setError("missing access_token in the response.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response = try {
|
|
|
|
|
|
|
|
|
|
// 認証されたアカウントのユーザ名を取得する
|
|
|
|
|
val request = Request.Builder()
|
|
|
|
|
.url("https://$instance/api/v1/accounts/verify_credentials")
|
|
|
|
|
.header("Authorization", "Bearer $access_token")
|
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
sendRequest(request)
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError(instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
|
|
|
|
return readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// アクセストークン手動入力でアカウントを更新する場合
|
|
|
|
|
// verify_credentialsを呼び出す
|
|
|
|
|
fun checkAccessToken(access_token : String) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(instance)
|
|
|
|
|
if(result.error != null) return result
|
|
|
|
|
|
|
|
|
|
val token_info = JSONObject()
|
|
|
|
|
val response = try {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// 指定されたアクセストークンを使って token_info を捏造する
|
|
|
|
|
token_info.put("access_token", access_token)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// 認証されたアカウントのユーザ名を取得する
|
|
|
|
|
val request = Request.Builder()
|
|
|
|
|
.url("https://$instance/api/v1/accounts/verify_credentials")
|
|
|
|
|
.header("Authorization", "Bearer $access_token")
|
|
|
|
|
.build()
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
sendRequest(request)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError(instance + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val r2 = readJson(result, response)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
r2?.jsonObject ?: return r2
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// credentialを読めたならtoken_infoを保存したい
|
|
|
|
|
result.token_info = token_info
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun getHttp(url : String) : TootApiResult? {
|
|
|
|
|
val result = TootApiResult.makeWithCaption(url)
|
|
|
|
|
if(result.error != null) return result
|
|
|
|
|
|
|
|
|
|
val response = try {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
sendRequest(Request.Builder().url(url).build(), url)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
return result.setError(url + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
if(callback.isApiCancelled) return null
|
2018-01-11 10:31:25 +01:00
|
|
|
|
val request = response.request()
|
|
|
|
|
if( request != null ){
|
|
|
|
|
callback.publishApiProgress(context.getString(R.string.reading_api, request.method(), url))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
result.readBodyString(response)
|
2018-01-11 10:31:25 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(callback.isApiCancelled) return null
|
2018-01-11 10:31:25 +01:00
|
|
|
|
callback.publishApiProgress(context.getString(R.string.parsing_response))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(result.isErrorOrEmptyBody()) return result
|
|
|
|
|
|
|
|
|
|
result.data = result.bodyString
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.trace(ex)
|
|
|
|
|
result.error = Utils.formatResponse(response, result.caption, result.bodyString ?: "no information")
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
private fun sendRequest(request : Request, showPath : String? = null) : Response {
|
|
|
|
|
callback.publishApiProgress(context.getString(R.string.request_api, request.method(), showPath ?: request.url().encodedPath()))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val call = ok_http_client.newCall(request)
|
|
|
|
|
call_callback?.onCallCreated(call)
|
|
|
|
|
return call.execute()
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
private fun readJson(result : TootApiResult, response : Response) : TootApiResult? {
|
|
|
|
|
try {
|
|
|
|
|
if(callback.isApiCancelled) return null
|
2018-01-11 10:31:25 +01:00
|
|
|
|
val request = response.request()
|
|
|
|
|
if( request != null ){
|
|
|
|
|
callback.publishApiProgress(context.getString(R.string.reading_api, request.method(), request.url().encodedPath()))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
result.readBodyString(response)
|
2018-01-11 10:31:25 +01:00
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(callback.isApiCancelled) return null
|
2018-01-11 10:31:25 +01:00
|
|
|
|
callback.publishApiProgress(context.getString(R.string.parsing_response))
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(result.isErrorOrEmptyBody()) return result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
val bodyString = result.bodyString
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(bodyString?.startsWith("[") == true) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
result.data = JSONArray(bodyString)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
} else if(bodyString?.startsWith("{") == true) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val json = JSONObject(bodyString)
|
|
|
|
|
val error = Utils.optStringX(json, "error")
|
|
|
|
|
if(error != null) {
|
|
|
|
|
result.error = "API returns error: $error"
|
|
|
|
|
} else {
|
|
|
|
|
result.data = json
|
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
} else {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
result.error = context.getString(R.string.response_not_json) + "\n" + bodyString
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
result.error = Utils.formatResponse(response, result.caption, result.bodyString ?: "no information")
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
// private fun parseResponse(tokenInfo : JSONObject?, response : Response) : TootApiResult? {
|
|
|
|
|
// try {
|
|
|
|
|
// if(callback.isApiCancelled) return null
|
|
|
|
|
//
|
|
|
|
|
// if(! response.isSuccessful) {
|
|
|
|
|
// return TootApiResult(response, Utils.formatResponse(response, instance ?: "(no instance)"))
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// val bodyString = response.body()?.string() ?: throw RuntimeException("missing response body.")
|
|
|
|
|
// if(callback.isApiCancelled) return null
|
|
|
|
|
//
|
|
|
|
|
// callback.publishApiProgress(context.getString(R.string.parsing_response))
|
|
|
|
|
// return if(bodyString.startsWith("{")) {
|
|
|
|
|
//
|
|
|
|
|
// val obj = JSONObject(bodyString)
|
|
|
|
|
//
|
|
|
|
|
// val error = Utils.optStringX(obj, "error")
|
|
|
|
|
//
|
|
|
|
|
// if(error != null)
|
|
|
|
|
// TootApiResult(context.getString(R.string.api_error, error))
|
|
|
|
|
// else
|
|
|
|
|
// TootApiResult(response, tokenInfo, bodyString, obj)
|
|
|
|
|
//
|
|
|
|
|
// } else if(bodyString.startsWith("[")) {
|
|
|
|
|
// val array = JSONArray(bodyString)
|
|
|
|
|
// TootApiResult(response, tokenInfo, bodyString, array)
|
|
|
|
|
// } else {
|
|
|
|
|
// TootApiResult(response, Utils.formatResponse(response, instance ?: "(no instance)", bodyString))
|
|
|
|
|
// }
|
|
|
|
|
// } catch(ex : Throwable) {
|
|
|
|
|
// TootApiClient.log.trace(ex)
|
|
|
|
|
// return TootApiResult(Utils.formatError(ex, "API data error"))
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// }
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|