Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-10-19 19:36:19 +03:00
parent 170a44c377
commit 5f7bffd6e4
17 changed files with 454 additions and 85 deletions

View File

@@ -15,7 +15,15 @@ import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit
class ApiException(message: String, val offline: Boolean = false) : Exception(message)
/**
* An error ready to show. [code] is the server's machine-readable reason (server/errors.js);
* [offline] means the data could not be fetched — no network, or the university's catalog
* is down — so a saved copy stays on screen and "Повторить" makes sense.
*/
class ApiException(message: String, val offline: Boolean = false, val code: String? = null) : Exception(message)
/** Server codes after which trying again later can help. */
private val SOURCE_DOWN = setOf("source_unavailable", "source_timeout", "busy")
val json = Json {
ignoreUnknownKeys = true
@@ -44,14 +52,20 @@ class Api(baseUrl: String, cacheDir: File? = null) {
client.newCall(Request.Builder().url(url).build()).execute().use { response ->
val text = response.body?.string().orEmpty()
if (!response.isSuccessful) {
val message = runCatching {
json.parseToJsonElement(text).jsonObject["error"]?.jsonPrimitive?.content
}.getOrNull()
// The server's catch-all 404 means it runs an older version without this endpoint.
if (response.code == 404 && path.startsWith("api/") && message == "Адрес не найден.") throw ApiException(
val body = runCatching { json.parseToJsonElement(text).jsonObject }.getOrNull()
val message = body?.get("error")?.jsonPrimitive?.content
// Servers before API version 5 send no code; their catch-all 404 has this text.
val code = body?.get("code")?.jsonPrimitive?.content
?: if (response.code == 404 && message == "Адрес не найден.") "unknown_endpoint" else null
if (code == "unknown_endpoint") throw ApiException(
"Сервер расписания не знает запрос /$path — на нём старая версия. Обновите сервер.",
code = code,
)
throw ApiException(
message ?: "Сервер ответил ошибкой ${response.code}.",
offline = code in SOURCE_DOWN,
code = code,
)
throw ApiException(message ?: "Сервер ответил ошибкой ${response.code}.")
}
text
}
@@ -70,12 +84,12 @@ class Api(baseUrl: String, cacheDir: File? = null) {
return call()
} catch (e: IOException) {
android.util.Log.w("Api", "GET $url failed (attempt ${attempt + 1})", e)
if (attempt == RETRIES - 1) throw ApiException(OFFLINE, offline = true)
if (attempt == RETRIES - 1) throw ApiException(OFFLINE, offline = true, code = "offline")
delay(wait)
wait *= 3
}
}
throw ApiException(OFFLINE, offline = true)
throw ApiException(OFFLINE, offline = true, code = "offline")
}
private companion object {