funkwhale-app-android/app/src/main/java/audio/funkwhale/ffa/utils/Extensions.kt

153 lines
4.7 KiB
Kotlin
Raw Normal View History

package audio.funkwhale.ffa.utils
2019-08-19 16:50:33 +02:00
2021-07-23 14:10:13 +02:00
import android.content.Context
2019-08-19 16:50:33 +02:00
import android.os.Build
import android.util.Log
2023-01-10 13:56:20 +01:00
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
2021-07-23 14:10:13 +02:00
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
2019-08-19 16:50:33 +02:00
import kotlinx.coroutines.Dispatchers.Main
2019-10-31 16:17:37 +01:00
import kotlinx.coroutines.flow.Flow
2019-08-19 16:50:33 +02:00
import kotlinx.coroutines.launch
2021-07-23 14:10:13 +02:00
import kotlinx.coroutines.runBlocking
import net.openid.appauth.ClientSecretPost
import java.text.SimpleDateFormat
2022-08-26 14:06:41 +02:00
import java.util.Date
2019-08-19 16:50:33 +02:00
import kotlin.coroutines.CoroutineContext
2021-07-23 14:10:13 +02:00
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) {
2019-10-31 16:17:37 +01:00
collect { data ->
callback(data.data, data.origin == Repository.Origin.Cache, data.page, data.hasMore)
2019-08-19 16:50:33 +02:00
}
}
}
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 {
2021-07-23 14:10:13 +02:00
return runBlocking {
this@authorize.apply {
if (!Settings.isAnonymous()) {
oAuth.state().let { state ->
state.accessTokenExpirationTime?.let {
Log.i("Request.authorize()", "Accesstoken expiration: ${Date(it).format()}")
}
2021-07-23 14:10:13 +02:00
val old = state.accessToken
val auth = ClientSecretPost(oAuth.state().clientSecret)
2021-07-23 14:10:13 +02:00
val done = CompletableDeferred<Boolean>()
2022-06-11 16:37:38 +02:00
val tokenService = oAuth.service(context)
2021-07-23 14:10:13 +02:00
2022-06-11 16:37:38 +02:00
state.performActionWithFreshTokens(tokenService, auth) { token, _, e ->
if (e != null) {
2022-08-26 14:06:41 +02:00
Log.e("Request.authorize()", "performActionWithFreshToken failed: $e")
2022-06-11 16:37:38 +02:00
Log.e("Request.authorize()", Log.getStackTraceString(e))
}
if (token == old) {
Log.i("Request.authorize()", "Accesstoken not renewed")
}
2021-07-23 14:10:13 +02:00
if (token != old && token != null) {
state.save()
}
header("Authorization", "Bearer ${oAuth.state().accessToken}")
2021-07-23 14:10:13 +02:00
done.complete(true)
}
done.await()
2022-06-11 16:37:38 +02:00
tokenService.dispose()
2021-07-23 14:10:13 +02:00
return@runBlocking this
}
}
}
}
}
fun FuelError.formatResponseMessage(): String {
return "${response.statusCode}: ${response.url}"
}
2021-07-23 14:10:13 +02:00
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)
2021-09-09 09:56:15 +02:00
}
2022-12-09 09:49:41 +01:00
fun String?.containsIgnoringCase(candidate: String): Boolean =
this != null && this.lowercase().contains(candidate.lowercase())
2023-01-10 13:56:20 +01:00
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