kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale-android
153 wiersze
4.7 KiB
Kotlin
153 wiersze
4.7 KiB
Kotlin
package audio.funkwhale.ffa.utils
|
|
|
|
import android.content.Context
|
|
import android.os.Build
|
|
import android.util.Log
|
|
import androidx.lifecycle.LiveData
|
|
import androidx.lifecycle.MediatorLiveData
|
|
import audio.funkwhale.ffa.model.DownloadInfo
|
|
import audio.funkwhale.ffa.repositories.Repository
|
|
import com.github.kittinunf.fuel.core.FuelError
|
|
import com.github.kittinunf.fuel.core.Request
|
|
import com.google.android.exoplayer2.offline.Download
|
|
import com.google.gson.Gson
|
|
import kotlinx.coroutines.CompletableDeferred
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers.Main
|
|
import kotlinx.coroutines.flow.Flow
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.runBlocking
|
|
import net.openid.appauth.ClientSecretPost
|
|
import java.text.SimpleDateFormat
|
|
import java.util.Date
|
|
import kotlin.coroutines.CoroutineContext
|
|
|
|
inline fun <D> Flow<Repository.Response<D>>.untilNetwork(
|
|
scope: CoroutineScope,
|
|
context: CoroutineContext = Main,
|
|
crossinline callback: (data: List<D>, isCache: Boolean, page: Int, hasMore: Boolean) -> Unit
|
|
) {
|
|
scope.launch(context) {
|
|
collect { data ->
|
|
callback(data.data, data.origin == Repository.Origin.Cache, data.page, data.hasMore)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun <T> Int.onApi(block: () -> T) {
|
|
if (Build.VERSION.SDK_INT >= this) {
|
|
block()
|
|
}
|
|
}
|
|
|
|
fun <T, U> Int.onApi(block: () -> T, elseBlock: (() -> U)) {
|
|
if (Build.VERSION.SDK_INT >= this) {
|
|
block()
|
|
} else {
|
|
elseBlock()
|
|
}
|
|
}
|
|
|
|
fun Request.authorize(context: Context, oAuth: OAuth): Request {
|
|
return runBlocking {
|
|
this@authorize.apply {
|
|
if (!Settings.isAnonymous()) {
|
|
oAuth.state().let { state ->
|
|
state.accessTokenExpirationTime?.let {
|
|
Log.i("Request.authorize()", "Accesstoken expiration: ${Date(it).format()}")
|
|
}
|
|
val old = state.accessToken
|
|
val auth = ClientSecretPost(oAuth.state().clientSecret)
|
|
val done = CompletableDeferred<Boolean>()
|
|
val tokenService = oAuth.service(context)
|
|
|
|
state.performActionWithFreshTokens(tokenService, auth) { token, _, e ->
|
|
if (e != null) {
|
|
Log.e("Request.authorize()", "performActionWithFreshToken failed: $e")
|
|
Log.e("Request.authorize()", Log.getStackTraceString(e))
|
|
}
|
|
if (token == old) {
|
|
Log.i("Request.authorize()", "Accesstoken not renewed")
|
|
}
|
|
if (token != old && token != null) {
|
|
state.save()
|
|
}
|
|
header("Authorization", "Bearer ${oAuth.state().accessToken}")
|
|
done.complete(true)
|
|
}
|
|
done.await()
|
|
tokenService.dispose()
|
|
return@runBlocking this
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun FuelError.formatResponseMessage(): String {
|
|
return "${response.statusCode}: ${response.url}"
|
|
}
|
|
|
|
fun Download.getMetadata(): DownloadInfo? =
|
|
Gson().fromJson(String(this.request.data), DownloadInfo::class.java)
|
|
|
|
val ISO_8601_DATE_TIME_FORMAT = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
|
|
|
|
fun Date.format(): String {
|
|
return ISO_8601_DATE_TIME_FORMAT.format(this)
|
|
}
|
|
|
|
fun String?.containsIgnoringCase(candidate: String): Boolean =
|
|
this != null && this.lowercase().contains(candidate.lowercase())
|
|
|
|
inline fun <T, U, V, R> LiveData<T>.mergeWith(
|
|
u: LiveData<U>,
|
|
v: LiveData<V>,
|
|
crossinline block: (valT: T, valU: U, valV: V) -> R
|
|
): LiveData<R> = MediatorLiveData<R>().apply {
|
|
addSource(this@mergeWith) {
|
|
if (u.value != null && v.value != null) {
|
|
postValue(block(it, u.value!!, v.value!!))
|
|
}
|
|
}
|
|
addSource(u) {
|
|
if (this@mergeWith.value != null && u.value != null) {
|
|
postValue(block(this@mergeWith.value!!, it, v.value!!))
|
|
}
|
|
}
|
|
addSource(v) {
|
|
if (this@mergeWith.value != null && u.value != null) {
|
|
postValue(block(this@mergeWith.value!!, u.value!!, it))
|
|
}
|
|
}
|
|
}
|
|
|
|
inline fun <T, U, V, W, R> LiveData<T>.mergeWith(
|
|
u: LiveData<U>,
|
|
v: LiveData<V>,
|
|
w: LiveData<W>,
|
|
crossinline block: (valT: T, valU: U, valV: V, valW: W) -> R
|
|
): LiveData<R> = MediatorLiveData<R>().apply {
|
|
addSource(this@mergeWith) {
|
|
if (u.value != null && v.value != null && w.value != null) {
|
|
postValue(block(it, u.value!!, v.value!!, w.value!!))
|
|
}
|
|
}
|
|
addSource(u) {
|
|
if (this@mergeWith.value != null && v.value != null && w.value != null) {
|
|
postValue(block(this@mergeWith.value!!, it, v.value!!, w.value!!))
|
|
}
|
|
}
|
|
addSource(v) {
|
|
if (this@mergeWith.value != null && u.value != null && w.value != null) {
|
|
postValue(block(this@mergeWith.value!!, u.value!!, it, w.value!!))
|
|
}
|
|
}
|
|
addSource(w) {
|
|
if (this@mergeWith.value != null && u.value != null && v.value != null) {
|
|
postValue(block(this@mergeWith.value!!, u.value!!, v.value!!, it))
|
|
}
|
|
}
|
|
}
|
|
|
|
public fun String?.toIntOrElse(default: Int): Int = this?.toIntOrNull(radix = 10) ?: default |