SpotlessAPply (#1729)

Co-authored-by: Dude so hot <djohn@fbi.gov>
This commit is contained in:
renovate[bot]andDude so hot authored and GitHub committed 2024-10-15 02:19:56 +00:00
1 parent 78b898dc5c
commit 52ca5a7eb8
27 files changed
+159 -206

No files matched your search

@@ -243,9 +243,8 @@ private class AnchoredHorizontalDraggableNode<T>(
private fun Offset.reverseIfNeeded() = if (isReverseDirection) this * -1f else this * 1f
}
internal fun <T> AnchoredDraggableState<T>.newOffsetForDelta(delta: Float) =
((if (offset.isNaN()) 0f else offset) + delta)
.coerceIn(anchors.minPosition(), anchors.maxPosition())
internal fun <T> AnchoredDraggableState<T>.newOffsetForDelta(delta: Float) = ((if (offset.isNaN()) 0f else offset) + delta)
.coerceIn(anchors.minPosition(), anchors.maxPosition())
/**
* Compute the target anchor based on the [currentOffset], [velocity] and [positionalThreshold] and
@@ -299,17 +298,16 @@ internal fun <T> anchoredDraggableFlingBehavior(
density: Density,
positionalThreshold: (totalDistance: Float) -> Float,
snapAnimationSpec: AnimationSpec<Float>,
): TargetedFlingBehavior =
snapFlingBehavior(
decayAnimationSpec = NoOpDecayAnimationSpec,
snapAnimationSpec = snapAnimationSpec,
snapLayoutInfoProvider =
AnchoredDraggableLayoutInfoProvider(
state = state,
positionalThreshold = positionalThreshold,
velocityThreshold = { with(density) { 125.dp.toPx() } },
),
)
): TargetedFlingBehavior = snapFlingBehavior(
decayAnimationSpec = NoOpDecayAnimationSpec,
snapAnimationSpec = snapAnimationSpec,
snapLayoutInfoProvider =
AnchoredDraggableLayoutInfoProvider(
state = state,
positionalThreshold = positionalThreshold,
velocityThreshold = { with(density) { 125.dp.toPx() } },
),
)
@Suppress("FunctionName")
@OptIn(ExperimentalFoundationApi::class)
@@ -317,25 +315,24 @@ private fun <T> AnchoredDraggableLayoutInfoProvider(
state: AnchoredDraggableState<T>,
positionalThreshold: (totalDistance: Float) -> Float,
velocityThreshold: () -> Float,
): SnapLayoutInfoProvider =
object : SnapLayoutInfoProvider {
): SnapLayoutInfoProvider = object : SnapLayoutInfoProvider {
// We never decay in AnchoredDraggable's fling
override fun calculateApproachOffset(velocity: Float, decayOffset: Float) = 0f
// We never decay in AnchoredDraggable's fling
override fun calculateApproachOffset(velocity: Float, decayOffset: Float) = 0f
override fun calculateSnapOffset(velocity: Float): Float {
val currentOffset = state.requireOffset()
val target =
state.anchors.computeTarget(
currentOffset = currentOffset,
currentValue = state.currentValue,
velocity = velocity,
positionalThreshold = positionalThreshold,
velocityThreshold = velocityThreshold,
)
return state.anchors.positionOf(target) - currentOffset
}
override fun calculateSnapOffset(velocity: Float): Float {
val currentOffset = state.requireOffset()
val target =
state.anchors.computeTarget(
currentOffset = currentOffset,
currentValue = state.currentValue,
velocity = velocity,
positionalThreshold = positionalThreshold,
velocityThreshold = velocityThreshold,
)
return state.anchors.positionOf(target) - currentOffset
}
}
private val NoOpDecayAnimationSpec: DecayAnimationSpec<Float> =
object : FloatDecayAnimationSpec {
@@ -206,11 +206,10 @@ class DrawerState2(
/**
* The default [Saver] implementation for [DrawerState2].
*/
fun Saver(confirmStateChange: (DrawerValue) -> Boolean) =
Saver<DrawerState2, DrawerValue>(
save = { it.currentValue },
restore = { DrawerState2(it, confirmStateChange) },
)
fun Saver(confirmStateChange: (DrawerValue) -> Boolean) = Saver<DrawerState2, DrawerValue>(
save = { it.currentValue },
restore = { DrawerState2(it, confirmStateChange) },
)
}
}
@@ -98,16 +98,14 @@ fun rememberSystemUiController(
}
@Composable
private fun findWindow(): Window? =
(LocalView.current.parent as? DialogWindowProvider)?.window
?: LocalView.current.context.findWindow()
private fun findWindow(): Window? = (LocalView.current.parent as? DialogWindowProvider)?.window
?: LocalView.current.context.findWindow()
private tailrec fun Context.findWindow(): Window? =
when (this) {
is Activity -> window
is ContextWrapper -> baseContext.findWindow()
else -> null
}
private tailrec fun Context.findWindow(): Window? = when (this) {
is Activity -> window
is ContextWrapper -> baseContext.findWindow()
else -> null
}
/**
* A helper class for setting the navigation and status bar colors for a [View], gracefully
@@ -635,8 +635,7 @@ data class WebViewError(
fun rememberWebViewState(
url: String,
additionalHttpHeaders: Map<String, String> = emptyMap(),
): WebViewState =
// Rather than using .apply {} here we will recreate the state, this prevents
): WebViewState = // Rather than using .apply {} here we will recreate the state, this prevents
// a recomposition loop when the webview updates the url itself.
remember {
WebViewState(
@@ -303,9 +303,8 @@ object EhEngine {
}.executeSafely { }
}
suspend fun getFavoriteNote(gid: Long, token: String) =
ehRequest(EhUrl.getAddFavorites(gid, token), EhUrl.getGalleryDetailUrl(gid, token))
.fetchUsingAsText(FavoritesParser::parseNote)
suspend fun getFavoriteNote(gid: Long, token: String) = ehRequest(EhUrl.getAddFavorites(gid, token), EhUrl.getGalleryDetailUrl(gid, token))
.fetchUsingAsText(FavoritesParser::parseNote)
suspend fun downloadArchive(gid: Long, token: String, or: String, res: String, isHAtH: Boolean): String? {
val url = EhUrl.getDownloadArchive(gid, token, or)
@@ -384,54 +383,51 @@ object EhEngine {
}
}.fetchUsingAsText(String::parseAs)
suspend fun fillGalleryListByApi(galleryInfoList: List<GalleryInfo>, referer: String? = null) =
galleryInfoList.chunked(MAX_REQUEST_SIZE).chunked(MAX_SEQUENTIAL_REQUESTS).forEachIndexed { index, chunk ->
if (index != 0) {
delay(REQUEST_INTERVAL)
}
chunk.parMap {
ehRequest(EhUrl.apiUrl, referer, EhUrl.origin) {
jsonBody {
put("method", "gdata")
array("gidlist") {
it.forEach {
addJsonArray {
add(it.gid)
add(it.token)
}
suspend fun fillGalleryListByApi(galleryInfoList: List<GalleryInfo>, referer: String? = null) = galleryInfoList.chunked(MAX_REQUEST_SIZE).chunked(MAX_SEQUENTIAL_REQUESTS).forEachIndexed { index, chunk ->
if (index != 0) {
delay(REQUEST_INTERVAL)
}
chunk.parMap {
ehRequest(EhUrl.apiUrl, referer, EhUrl.origin) {
jsonBody {
put("method", "gdata")
array("gidlist") {
it.forEach {
addJsonArray {
add(it.gid)
add(it.token)
}
}
put("namespace", 1)
}
}.fetchUsingAsText { GalleryApiParser.parse(this, it) }
}
put("namespace", 1)
}
}.fetchUsingAsText { GalleryApiParser.parse(this, it) }
}
}
suspend fun voteComment(apiUid: Long, apiKey: String?, gid: Long, token: String, commentId: Long, commentVote: Int): VoteCommentResult =
ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
jsonBody {
put("method", "votecomment")
put("apiuid", apiUid)
put("apikey", requireNotNull(apiKey))
put("gid", gid)
put("token", token)
put("comment_id", commentId)
put("comment_vote", commentVote)
}
}.fetchUsingAsText(String::parseAs)
suspend fun voteComment(apiUid: Long, apiKey: String?, gid: Long, token: String, commentId: Long, commentVote: Int): VoteCommentResult = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
jsonBody {
put("method", "votecomment")
put("apiuid", apiUid)
put("apikey", requireNotNull(apiKey))
put("gid", gid)
put("token", token)
put("comment_id", commentId)
put("comment_vote", commentVote)
}
}.fetchUsingAsText(String::parseAs)
suspend fun voteTag(apiUid: Long, apiKey: String?, gid: Long, token: String, tags: String, vote: Int) =
ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
jsonBody {
put("method", "taggallery")
put("apiuid", apiUid)
put("apikey", requireNotNull(apiKey))
put("gid", gid)
put("token", token)
put("tags", tags)
put("vote", vote)
}
}.fetchUsingAsText(VoteTagParser::parse)
suspend fun voteTag(apiUid: Long, apiKey: String?, gid: Long, token: String, tags: String, vote: Int) = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
jsonBody {
put("method", "taggallery")
put("apiuid", apiUid)
put("apikey", requireNotNull(apiKey))
put("gid", gid)
put("token", token)
put("tags", tags)
put("vote", vote)
}
}.fetchUsingAsText(VoteTagParser::parse)
suspend fun getGalleryToken(gid: Long, gtoken: String, page: Int) = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
jsonBody {
@@ -125,8 +125,7 @@ object EhTagDatabase : CoroutineScope {
private fun String.removeSpace(): String = replace(" ", "")
private fun String.containsIgnoreSpace(other: String, ignoreCase: Boolean = true): Boolean =
removeSpace().contains(other.removeSpace(), ignoreCase)
private fun String.containsIgnoreSpace(other: String, ignoreCase: Boolean = true): Boolean = removeSpace().contains(other.removeSpace(), ignoreCase)
private fun getMetadata(context: Context): Array<String>? = context.resources.getStringArray(R.array.tag_translation_metadata)
.takeIf { it.size == 4 }
@@ -108,11 +108,10 @@ object EhUrl {
else -> URL_IMAGE_SEARCH_E
}
fun getGalleryDetailUrl(gid: Long, token: String, index: Int = 0, allComment: Boolean = false) =
ehUrl(listOf("g", "$gid", token)) {
if (index != 0) addQueryParameter("p", "$index")
if (allComment) addQueryParameter("hc", "1")
}.buildString()
fun getGalleryDetailUrl(gid: Long, token: String, index: Int = 0, allComment: Boolean = false) = ehUrl(listOf("g", "$gid", token)) {
if (index != 0) addQueryParameter("p", "$index")
if (allComment) addQueryParameter("hc", "1")
}.buildString()
fun getGalleryMultiPageViewerUrl(gid: Long, token: String) = ehUrl(listOf("mpv", "$gid", token)).buildString()
@@ -4,6 +4,5 @@ import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.HttpClientEngineFactory
object Cronet : HttpClientEngineFactory<CronetConfig> {
override fun create(block: CronetConfig.() -> Unit): HttpClientEngine =
CronetEngine(CronetConfig().apply(block))
override fun create(block: CronetConfig.() -> Unit): HttpClientEngine = CronetEngine(CronetConfig().apply(block))
}
@@ -315,8 +315,7 @@ class SpiderDen(val info: GalleryInfo) {
return archived
}
suspend fun exportAsCbz(file: Path) =
downloadDir!!.find(archiveName)?.sendTo(file) ?: archiveTo(file)
suspend fun exportAsCbz(file: Path) = downloadDir!!.find(archiveName)?.sendTo(file) ?: archiveTo(file)
private suspend fun archiveTo(file: Path) = resourceScope {
val comicInfo = closeable {
@@ -106,8 +106,7 @@ suspend fun keepNoMediaFileStatus(downloadDir: Path = downloadLocation) {
}
}
fun getFavoriteIcon(favorited: Boolean) =
if (favorited) Icons.Default.Favorite else Icons.Default.FavoriteBorder
fun getFavoriteIcon(favorited: Boolean) = if (favorited) Icons.Default.Favorite else Icons.Default.FavoriteBorder
suspend fun DialogState.startDownload(
context: Context,
@@ -281,14 +280,11 @@ suspend fun removeFromFavorites(galleryInfo: BaseGalleryInfo) = doModifyFavorite
localFavorited = EhDB.containLocalFavorites(galleryInfo.gid),
)
fun DestinationsNavigator.navToReader(info: BaseGalleryInfo, page: Int = -1) =
navToReader(ReaderScreenArgs.Gallery(info, page))
fun DestinationsNavigator.navToReader(info: BaseGalleryInfo, page: Int = -1) = navToReader(ReaderScreenArgs.Gallery(info, page))
fun DestinationsNavigator.navToReader(uri: Uri) =
navToReader(ReaderScreenArgs.Archive(uri))
fun DestinationsNavigator.navToReader(uri: Uri) = navToReader(ReaderScreenArgs.Archive(uri))
private fun DestinationsNavigator.navToReader(args: ReaderScreenArgs) =
navigate(ReaderScreenDestination(args)) { launchSingleTop = true }
private fun DestinationsNavigator.navToReader(args: ReaderScreenArgs) = navigate(ReaderScreenDestination(args)) { launchSingleTop = true }
context(DialogState, Context, DestinationsNavigator)
suspend fun doGalleryInfoAction(info: BaseGalleryInfo) {
@@ -94,8 +94,7 @@ class SecurityActivity : AppCompatActivity() {
result: BiometricPrompt.AuthenticationResult,
) = onAuthenticationSucceeded(activity, result)
override fun onAuthenticationFailed(activity: FragmentActivity?) =
onAuthenticationFailed(activity)
override fun onAuthenticationFailed(activity: FragmentActivity?) = onAuthenticationFailed(activity)
}
private fun onSuccess() {
@@ -238,21 +238,19 @@ private fun PageContainer(
}
}
private suspend fun ZoomableState?.panLeft(distance: Float, bounds: Rect): Boolean =
if (canPan { it.right - bounds.right }) {
panBy(Offset(-distance, 0f))
true
} else {
false
}
private suspend fun ZoomableState?.panLeft(distance: Float, bounds: Rect): Boolean = if (canPan { it.right - bounds.right }) {
panBy(Offset(-distance, 0f))
true
} else {
false
}
private suspend fun ZoomableState?.panRight(distance: Float, bounds: Rect): Boolean =
if (canPan { bounds.left - it.left }) {
panBy(Offset(distance, 0f))
true
} else {
false
}
private suspend fun ZoomableState?.panRight(distance: Float, bounds: Rect): Boolean = if (canPan { bounds.left - it.left }) {
panBy(Offset(distance, 0f))
true
} else {
false
}
private inline fun ZoomableState?.canPan(getRemaining: (Rect) -> Float): Boolean {
// TODO: Remove when K2 mode in IDE is stable
@@ -14,11 +14,10 @@ data class DownloadsFilterState(
val keyword: String = "",
) : Parcelable
fun DownloadsFilterState.take(info: DownloadInfo) =
mode.take(info, label) &&
(state == -1 || info.state == state) &&
with(info) {
title.containsIgnoreCase(keyword) ||
titleJpn.containsIgnoreCase(keyword) ||
simpleTags?.any { it.containsIgnoreCase(keyword) } ?: false
}
fun DownloadsFilterState.take(info: DownloadInfo) = mode.take(info, label) &&
(state == -1 || info.state == state) &&
with(info) {
title.containsIgnoreCase(keyword) ||
titleJpn.containsIgnoreCase(keyword) ||
simpleTags?.any { it.containsIgnoreCase(keyword) } ?: false
}
@@ -95,8 +95,7 @@ abstract class Suggestion {
open val canOpenDirectly: Boolean = false
}
suspend fun SearchDao.suggestions(prefix: String, limit: Int) =
(if (prefix.isBlank()) list(limit) else rawSuggestions(prefix, limit))
suspend fun SearchDao.suggestions(prefix: String, limit: Int) = (if (prefix.isBlank()) list(limit) else rawSuggestions(prefix, limit))
context(DialogState, DestinationsNavigator)
@Composable
@@ -2,5 +2,4 @@ package com.hippo.ehviewer.ui.tools
import androidx.compose.ui.Modifier
inline fun Modifier.thenIf(condition: Boolean, crossinline block: Modifier.() -> Modifier) =
if (condition) block() else this
inline fun Modifier.thenIf(condition: Boolean, crossinline block: Modifier.() -> Modifier) = if (condition) block() else this
@@ -29,8 +29,7 @@ value class TransitionsVisibilityScope(val scopes: Set<AnimatedVisibilityScope>)
context(TransitionsVisibilityScope)
@Composable
inline fun <T> togetherWith(scope: AnimatedVisibilityScope, block: @Composable TransitionsVisibilityScope.() -> T) =
block(remember(scopes, scope) { TransitionsVisibilityScope(scopes + scope) })
inline fun <T> togetherWith(scope: AnimatedVisibilityScope, block: @Composable TransitionsVisibilityScope.() -> T) = block(remember(scopes, scope) { TransitionsVisibilityScope(scopes + scope) })
context(SharedTransitionScope, TransitionsVisibilityScope, SETNodeGenerator)
@Composable
@@ -66,22 +66,21 @@ object AppUpdater {
return null
}
suspend fun downloadUpdate(url: String, file: File) =
ghStatement(url).executeSafely { response ->
response.status.ensureSuccess()
if (url.endsWith("zip")) {
response.bodyAsChannel().toInputStream().use { stream ->
ZipInputStream(stream).use { zip ->
zip.nextEntry
file.outputStream().use {
zip.copyTo(it)
}
suspend fun downloadUpdate(url: String, file: File) = ghStatement(url).executeSafely { response ->
response.status.ensureSuccess()
if (url.endsWith("zip")) {
response.bodyAsChannel().toInputStream().use { stream ->
ZipInputStream(stream).use { zip ->
zip.nextEntry
file.outputStream().use {
zip.copyTo(it)
}
}
} else {
response.bodyAsChannel().copyTo(file)
}
} else {
response.bodyAsChannel().copyTo(file)
}
}
}
private suspend inline fun ghStatement(url: String) = ktorClient.prepareGet(url) {
@@ -51,8 +51,7 @@ object AppConfig {
return null
}
private fun getDirInExternalAppDir(filename: String, create: Boolean = true) =
externalAppDir?.run { File(this, filename).takeIf { if (create) it.ensureDirectory() else it.isDirectory } }
private fun getDirInExternalAppDir(filename: String, create: Boolean = true) = externalAppDir?.run { File(this, filename).takeIf { if (create) it.ensureDirectory() else it.isDirectory } }
val defaultDownloadDir: File?
get() = getDirInExternalAppDir(DOWNLOAD, false)
@@ -8,11 +8,8 @@ import kotlinx.datetime.atStartOfDayIn
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime
fun LocalDate.toEpochMillis(timeZone: TimeZone = TimeZone.UTC): Long =
atStartOfDayIn(timeZone).toEpochMilliseconds()
fun LocalDate.toEpochMillis(timeZone: TimeZone = TimeZone.UTC): Long = atStartOfDayIn(timeZone).toEpochMilliseconds()
fun LocalDateTime.toEpochMillis(timeZone: TimeZone = TimeZone.UTC): Long =
toInstant(timeZone).toEpochMilliseconds()
fun LocalDateTime.toEpochMillis(timeZone: TimeZone = TimeZone.UTC): Long = toInstant(timeZone).toEpochMilliseconds()
fun Long.toLocalDateTime(timeZone: TimeZone = TimeZone.UTC): LocalDateTime =
Instant.fromEpochMilliseconds(this).toLocalDateTime(timeZone)
fun Long.toLocalDateTime(timeZone: TimeZone = TimeZone.UTC): LocalDateTime = Instant.fromEpochMilliseconds(this).toLocalDateTime(timeZone)
@@ -12,8 +12,7 @@ import java.io.FileDescriptor
import okio.Path
@RequiresApi(Build.VERSION_CODES.P)
private fun sendFileTotally(from: FileDescriptor, to: FileDescriptor): Long =
Os.sendfile(to, from, Int64Ref(0), Long.MAX_VALUE)
private fun sendFileTotally(from: FileDescriptor, to: FileDescriptor): Long = Os.sendfile(to, from, Int64Ref(0), Long.MAX_VALUE)
infix fun Path.sendTo(file: Path): Long {
if (isAtLeastP) {
@@ -28,8 +28,7 @@ object FileUtils {
// ext4 through a FUSE layer, so use that limit.
private const val MAX_FILENAME_BYTES = 255
fun ensureDirectory(file: File?) =
file?.let { if (it.exists()) it.isDirectory else it.mkdirs() } ?: false
fun ensureDirectory(file: File?) = file?.let { if (it.exists()) it.isDirectory else it.mkdirs() } ?: false
/**
* Convert byte to human readable string.<br></br>
@@ -81,22 +80,21 @@ object FileUtils {
}
}
private fun isValidFatFilenameChar(c: Char) =
when (c.code) {
in 0x00..0x1F,
'"'.code,
'*'.code,
'/'.code,
':'.code,
'<'.code,
'>'.code,
'?'.code,
'\\'.code,
'|'.code,
0x7F,
-> false
else -> true
}
private fun isValidFatFilenameChar(c: Char) = when (c.code) {
in 0x00..0x1F,
'"'.code,
'*'.code,
'/'.code,
':'.code,
'<'.code,
'>'.code,
'?'.code,
'\\'.code,
'|'.code,
0x7F,
-> false
else -> true
}
// From https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/FileUtils.java;l=1142;drc=7b647e4ea0e92f33c19b315eaed364ee067ba0aa
/**
@@ -135,8 +133,7 @@ object FileUtils {
* @param filename the complete filename
* @return null for can't find extension
*/
fun getExtensionFromFilename(filename: String?) =
filename?.substringAfterLast('.', "")?.ifEmpty { null }
fun getExtensionFromFilename(filename: String?) = filename?.substringAfterLast('.', "")?.ifEmpty { null }
/**
* Get name from filename
@@ -144,8 +141,7 @@ object FileUtils {
* @param filename the complete filename
* @return null for start with . dot
*/
fun getNameFromFilename(filename: String?) =
filename?.substringBeforeLast('.')?.ifEmpty { null }
fun getNameFromFilename(filename: String?) = filename?.substringBeforeLast('.')?.ifEmpty { null }
/**
* Create a temp file, you need to delete it by you self.
@@ -116,8 +116,7 @@ abstract class RangeSet<T : Comparable<T>> :
* @param elements ranges of values to determine presence of
* @return whether all values are all contained within the set
*/
override fun containsAll(elements: Collection<ClosedRange<T>>): Boolean =
elements.all { contains(it) }
override fun containsAll(elements: Collection<ClosedRange<T>>): Boolean = elements.all { contains(it) }
/**
* @return whether the set contains any values
@@ -34,8 +34,7 @@ class IntRangeSet : RangeSet<Int> {
private constructor(rangeSet: IntRangeSet) : super(rangeSet)
override fun createRange(start: Int, endInclusive: Int): IntRange =
IntRange(start, endInclusive)
override fun createRange(start: Int, endInclusive: Int): IntRange = IntRange(start, endInclusive)
override fun incrementValue(value: Int): Int = value + 1
@@ -156,6 +156,5 @@ object ReadableTime {
}
}
fun getFilenamableTime(time: Instant = Clock.System.now()): String =
FILENAMABLE_DATE_FORMAT.format(time.toLocalDateTime(TimeZone.currentSystemDefault()))
fun getFilenamableTime(time: Instant = Clock.System.now()): String = FILENAMABLE_DATE_FORMAT.format(time.toLocalDateTime(TimeZone.currentSystemDefault()))
}
+2 -4
View File
@@ -29,11 +29,9 @@ fun Path.moveTo(target: Path) = SystemFileSystem.atomicMove(this, target)
fun Path.openFileDescriptor(mode: String) = SystemFileSystem.openFileDescriptor(this, mode)
fun Path.openInputStream(): FileInputStream =
ParcelFileDescriptor.AutoCloseInputStream(SystemFileSystem.openFileDescriptor(this, "r"))
fun Path.openInputStream(): FileInputStream = ParcelFileDescriptor.AutoCloseInputStream(SystemFileSystem.openFileDescriptor(this, "r"))
fun Path.openOutputStream(): FileOutputStream =
ParcelFileDescriptor.AutoCloseOutputStream(SystemFileSystem.openFileDescriptor(this, "wt"))
fun Path.openOutputStream(): FileOutputStream = ParcelFileDescriptor.AutoCloseOutputStream(SystemFileSystem.openFileDescriptor(this, "wt"))
fun Path.toUri(): Uri {
val str = toString()
@@ -4,11 +4,9 @@ import androidx.compose.runtime.Composable
import java.math.BigDecimal
import java.math.RoundingMode
fun Any?.ifNotNullThen(content: @Composable () -> Unit): (@Composable () -> Unit)? =
if (this != null) content else null
fun Any?.ifNotNullThen(content: @Composable () -> Unit): (@Composable () -> Unit)? = if (this != null) content else null
fun Boolean.ifTrueThen(content: @Composable () -> Unit): (@Composable () -> Unit)? =
if (this) content else null
fun Boolean.ifTrueThen(content: @Composable () -> Unit): (@Composable () -> Unit)? = if (this) content else null
fun roundToDP(value: Double, places: Int): Double = BigDecimal(value).setScale(places, RoundingMode.HALF_EVEN).toDouble()
@@ -7,18 +7,14 @@ import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job =
launch(Dispatchers.Main, block = block)
fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job = launch(Dispatchers.Main, block = block)
fun CoroutineScope.launchIO(block: suspend CoroutineScope.() -> Unit): Job =
launch(Dispatchers.IO, block = block)
fun CoroutineScope.launchIO(block: suspend CoroutineScope.() -> Unit): Job = launch(Dispatchers.IO, block = block)
fun CoroutineScope.launchNonCancellable(block: suspend CoroutineScope.() -> Unit): Job =
launchIO { withContext(NonCancellable, block) }
fun CoroutineScope.launchNonCancellable(block: suspend CoroutineScope.() -> Unit): Job = launchIO { withContext(NonCancellable, block) }
suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.Main, block)
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)
suspend fun <T> withNonCancellableContext(block: suspend CoroutineScope.() -> T) =
withContext(NonCancellable, block)
suspend fun <T> withNonCancellableContext(block: suspend CoroutineScope.() -> T) = withContext(NonCancellable, block)