cloudstream/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt

214 lines
6.7 KiB
Kotlin
Raw Normal View History

2021-05-12 21:51:02 +00:00
package com.lagradost.cloudstream3.mvvm
import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
2021-07-08 18:03:17 +00:00
import com.bumptech.glide.load.HttpException
import com.lagradost.cloudstream3.BuildConfig
2021-08-04 01:50:24 +00:00
import com.lagradost.cloudstream3.ErrorLoadingException
2022-08-18 00:54:05 +00:00
import kotlinx.coroutines.*
2022-09-13 08:52:51 +00:00
import java.io.InterruptedIOException
2021-07-08 18:03:17 +00:00
import java.net.SocketTimeoutException
import java.net.UnknownHostException
2021-12-21 18:58:07 +00:00
import javax.net.ssl.SSLHandshakeException
2022-08-18 00:54:05 +00:00
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
2021-05-12 21:51:02 +00:00
const val DEBUG_EXCEPTION = "THIS IS A DEBUG EXCEPTION!"
2022-09-23 12:20:53 +00:00
const val DEBUG_PRINT = "DEBUG PRINT"
class DebugException(message: String) : Exception("$DEBUG_EXCEPTION\n$message")
inline fun debugException(message: () -> String) {
if (BuildConfig.DEBUG) {
throw DebugException(message.invoke())
}
}
2022-09-23 12:20:53 +00:00
inline fun debugPrint(tag: String = DEBUG_PRINT, message: () -> String) {
if (BuildConfig.DEBUG) {
Log.d(tag, message.invoke())
}
}
inline fun debugWarning(message: () -> String) {
if (BuildConfig.DEBUG) {
logError(DebugException(message.invoke()))
}
}
inline fun debugAssert(assert: () -> Boolean, message: () -> String) {
if (BuildConfig.DEBUG && assert.invoke()) {
throw DebugException(message.invoke())
}
}
inline fun debugWarning(assert: () -> Boolean, message: () -> String) {
if (BuildConfig.DEBUG && assert.invoke()) {
logError(DebugException(message.invoke()))
}
}
2023-01-17 15:57:46 +00:00
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
2021-08-29 18:42:44 +00:00
liveData.observe(this) { it?.let { t -> action(t) } }
2021-05-12 21:51:02 +00:00
}
2023-01-17 15:57:46 +00:00
fun <T> LifecycleOwner.observeNullable(liveData: LiveData<T>, action: (t: T) -> Unit) {
liveData.observe(this) { action(it) }
}
2022-08-03 00:04:03 +00:00
inline fun <reified T : Any> some(value: T?): Some<T> {
return if (value == null) {
Some.None
} else {
Some.Success(value)
}
}
sealed class Some<out T> {
data class Success<out T>(val value: T) : Some<T>()
object None : Some<Nothing>()
override fun toString(): String {
2022-08-18 00:54:05 +00:00
return when (this) {
2022-08-03 00:04:03 +00:00
is None -> "None"
is Success -> "Some(${value.toString()})"
}
}
}
sealed class ResourceSome<out T> {
data class Success<out T>(val value: T) : ResourceSome<T>()
object None : ResourceSome<Nothing>()
data class Loading(val data: Any? = null) : ResourceSome<Nothing>()
}
2021-05-12 21:51:02 +00:00
sealed class Resource<out T> {
data class Success<out T>(val value: T) : Resource<T>()
data class Failure(
val isNetworkError: Boolean,
val errorCode: Int?,
val errorResponse: Any?, //ResponseBody
val errorString: String,
) : Resource<Nothing>()
2021-08-04 01:50:24 +00:00
data class Loading(val url: String? = null) : Resource<Nothing>()
2021-05-12 21:51:02 +00:00
}
2021-06-17 16:20:05 +00:00
fun logError(throwable: Throwable) {
Log.d("ApiError", "-------------------------------------------------------------------")
Log.d("ApiError", "safeApiCall: " + throwable.localizedMessage)
Log.d("ApiError", "safeApiCall: " + throwable.message)
throwable.printStackTrace()
Log.d("ApiError", "-------------------------------------------------------------------")
}
2021-08-04 01:50:24 +00:00
fun <T> normalSafeApiCall(apiCall: () -> T): T? {
2021-06-17 16:20:05 +00:00
return try {
apiCall.invoke()
} catch (throwable: Throwable) {
logError(throwable)
return null
}
}
suspend fun <T> suspendSafeApiCall(apiCall: suspend () -> T): T? {
return try {
apiCall.invoke()
} catch (throwable: Throwable) {
logError(throwable)
return null
}
}
2021-12-21 18:58:07 +00:00
fun <T> safeFail(throwable: Throwable): Resource<T> {
val stackTraceMsg =
(throwable.localizedMessage ?: "") + "\n\n" + throwable.stackTrace.joinToString(
separator = "\n"
) {
"${it.fileName} ${it.lineNumber}"
}
2021-12-21 18:58:07 +00:00
return Resource.Failure(false, null, null, stackTraceMsg)
}
2022-08-18 00:54:05 +00:00
fun CoroutineScope.launchSafe(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val obj: suspend CoroutineScope.() -> Unit = {
try {
block()
2022-08-25 01:59:20 +00:00
} catch (throwable: Throwable) {
logError(throwable)
2022-08-18 00:54:05 +00:00
}
}
return this.launch(context, start, obj)
}
2021-05-12 21:51:02 +00:00
suspend fun <T> safeApiCall(
apiCall: suspend () -> T,
): Resource<T> {
return withContext(Dispatchers.IO) {
try {
Resource.Success(apiCall.invoke())
} catch (throwable: Throwable) {
2021-06-17 16:20:05 +00:00
logError(throwable)
2021-05-12 21:51:02 +00:00
when (throwable) {
2021-12-21 18:58:07 +00:00
is NullPointerException -> {
for (line in throwable.stackTrace) {
2021-12-27 17:43:15 +00:00
if (line?.fileName?.endsWith("provider.kt", ignoreCase = true) == true) {
2021-12-21 18:58:07 +00:00
return@withContext Resource.Failure(
false,
null,
null,
"NullPointerException at ${line.fileName} ${line.lineNumber}\nSite might have updated or added Cloudflare/DDOS protection"
)
}
}
safeFail(throwable)
}
2022-09-13 08:52:51 +00:00
is SocketTimeoutException, is InterruptedIOException -> {
Resource.Failure(
true,
null,
null,
"Connection Timeout\nPlease try again later."
)
2021-07-08 18:03:17 +00:00
}
is HttpException -> {
Resource.Failure(
false,
throwable.statusCode,
null,
throwable.message ?: "HttpException"
)
2021-07-08 18:03:17 +00:00
}
is UnknownHostException -> {
Resource.Failure(true, null, null, "Cannot connect to server, try again later.")
2021-05-12 21:51:02 +00:00
}
2021-07-29 00:19:42 +00:00
is ErrorLoadingException -> {
Resource.Failure(
true,
null,
null,
throwable.message ?: "Error loading, try again later."
)
2021-07-29 00:19:42 +00:00
}
2021-08-21 21:06:24 +00:00
is NotImplementedError -> {
Resource.Failure(false, null, null, "This operation is not implemented.")
}
2021-12-21 18:58:07 +00:00
is SSLHandshakeException -> {
Resource.Failure(
true,
null,
null,
2022-09-13 08:52:51 +00:00
(throwable.message ?: "SSLHandshakeException") + "\nTry a VPN or DNS."
2021-12-21 18:58:07 +00:00
)
2021-05-12 21:51:02 +00:00
}
2021-12-21 18:58:07 +00:00
else -> safeFail(throwable)
2021-05-12 21:51:02 +00:00
}
}
}
}