revanced-manager-compose/app/src/main/java/app/revanced/manager/util/Util.kt

109 lines
3.4 KiB
Kotlin

package app.revanced.manager.util
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.PackageManager.NameNotFoundException
import android.graphics.drawable.Drawable
import android.util.Log
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.compose.ui.graphics.Color
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import io.ktor.http.Url
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
import java.util.Locale
typealias PatchesSelection = Map<Int, Set<String>>
typealias Options = Map<Int, Map<String, Map<String, Any?>>>
fun Context.openUrl(url: String) {
startActivity(Intent(Intent.ACTION_VIEW, url.toUri()).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}
fun Context.loadIcon(string: String): Drawable? {
return try {
packageManager.getApplicationIcon(string)
} catch (e: NameNotFoundException) {
null
}
}
fun Context.toast(string: String, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, string, duration).show()
}
fun String.parseUrlOrNull() = try {
Url(this)
} catch (_: Throwable) {
null
}
/**
* Safely perform an operation that may fail to avoid crashing the app.
* If [block] fails, the error will be logged and a toast will be shown to the user to inform them that the action failed.
*
* @param context The android [Context].
* @param toastMsg The toast message to show if [block] throws.
* @param logMsg The log message.
* @param block The code to execute.
*/
inline fun uiSafe(context: Context, @StringRes toastMsg: Int, logMsg: String, block: () -> Unit) {
try {
block()
} catch (error: Exception) {
context.toast(
context.getString(
toastMsg,
error.message ?: error.cause?.message ?: error::class.simpleName
)
)
Log.e(tag, logMsg, error)
}
}
inline fun LifecycleOwner.launchAndRepeatWithViewLifecycle(
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
crossinline block: suspend CoroutineScope.() -> Unit
) {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(minActiveState) {
block()
}
}
}
/**
* Run [transformer] on the [Iterable] and then [combine] the result using [combiner].
* This is used to transform collections that contain [Flow]s into something that is easier to work with.
*/
@OptIn(ExperimentalCoroutinesApi::class)
inline fun <T, reified R, C> Flow<Iterable<T>>.flatMapLatestAndCombine(
crossinline combiner: (Array<R>) -> C,
crossinline transformer: (T) -> Flow<R>,
): Flow<C> = flatMapLatest { iterable ->
combine(iterable.map(transformer)) {
combiner(it)
}
}
val Color.hexCode: String
inline get() {
val a: Int = (alpha * 255).toInt()
val r: Int = (red * 255).toInt()
val g: Int = (green * 255).toInt()
val b: Int = (blue * 255).toInt()
return java.lang.String.format(Locale.getDefault(), "%02X%02X%02X%02X", r, g, b, a)
}