ref: e200ec2c2898c340acd1e8aca5452e0dd4acf877
app/src/main/java/ml/adamsprogs/bimba/api/Api.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
package ml.adamsprogs.bimba.api import android.net.ConnectivityManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ml.adamsprogs.bimba.R import java.io.IOException import java.io.InputStream import java.net.HttpURLConnection import java.net.URL import java.net.URLEncoder data class Server(val host: String, val token: String, val feeds: String) data class Result(val stream: InputStream?, val error: Error?) data class Error(val statusCode: Int, val stringResource: Int, val imageResource: Int) @Suppress("BlockingMethodInNonBlockingContext") suspend fun getFeeds(cm: ConnectivityManager, server: Server): Result { return rawRequest( URL("${hostWithScheme(server.host)}/api/"), server, cm ) // todo(error-handling) if is not a valid URL } suspend fun queryItems( cm: ConnectivityManager, server: Server, query: String, limit: Int? = null ): Result { val params = mutableMapOf("q" to query) if (limit != null) { params["limit"] = limit.toString() } return request(server, "items", params, cm) } suspend fun locateItems(cm: ConnectivityManager, server: Server, plusCode: String): Result { return request(server, "items", mapOf("near" to plusCode), cm) } suspend fun getDepartures( cm: ConnectivityManager, server: Server, stop: String, line: String? = null ): Result { val params = mutableMapOf("code" to stop) if (line != null) { params["line"] = line } return request(server, "departures", params, cm) } @Suppress("BlockingMethodInNonBlockingContext") suspend fun rawRequest(url: URL, server: Server, cm: ConnectivityManager): Result { @Suppress("DEPRECATION") // fix_later(API_29, API_23) https://developer.android.com/reference/android/net/ConnectivityManager#getActiveNetwork() if (cm.activeNetworkInfo == null) { // todo check false-positives return Result(null, Error(0, R.string.error_offline, R.drawable.error_net)) } return withContext(Dispatchers.IO) { val c = (url.openConnection() as HttpURLConnection).apply { setRequestProperty("X-Bimba-Token", server.token) } try { if (c.responseCode == 200) { Result(c.inputStream, null) } else { val (string, image) = when (c.responseCode) { 400 -> Pair(R.string.error_400, R.drawable.error_app) 401 -> Pair(R.string.error_401, R.drawable.error_sec) 403 -> Pair(R.string.error_403, R.drawable.error_sec) 404 -> Pair( R.string.error_404, R.drawable.error_search ) // todo check if server returns 404 429 -> Pair(R.string.error_429, R.drawable.error_limit) 500 -> Pair(R.string.error_50x, R.drawable.error_server) 502 -> Pair(R.string.error_50x, R.drawable.error_server) 503 -> Pair(R.string.error_50x, R.drawable.error_server) 504 -> Pair(R.string.error_50x, R.drawable.error_server) else -> Pair(R.string.error_unknown, R.drawable.error_other) } Result(c.errorStream, Error(c.responseCode, string, image)) } } catch (e: IOException) { Result(null, Error(0, R.string.error_connecting, R.drawable.error_server)) } } } @Suppress("BlockingMethodInNonBlockingContext") suspend fun request( server: Server, resource: String, params: Map<String, String>, cm: ConnectivityManager ): Result { return withContext(Dispatchers.IO) { val url = URL( "${hostWithScheme(server.host)}/api/${server.feeds}/$resource${ params.map { "${it.key}=${ URLEncoder.encode( it.value, "utf-8" ) }" }.joinToString("&", "?") }" ) rawRequest(url, server, cm) } } fun hostWithScheme(host: String): String = if (host.startsWith("http://") or host.startsWith("https://")) { host } else { "https://$host" } |