Compare commits

..
28 Commits
Author SHA1 Message Date
tarsin 25d4d5e393 Bump to 1.8.8.0
Releases / Build (push) Has been cancelled
2023-05-25 15:33:07 +08:00
tarsin 1743caa869 Remove unused padding 2023-05-25 15:13:27 +08:00
tarsin 958fdb19b4 Revert "Decode cf protected email, step 1"
This reverts commit d44981a70c.
2023-05-25 15:02:14 +08:00
tarsin 4b63648a79 Revert "Decode cf protected email, step 2, Resolve #1102"
This reverts commit 74c6bfa479.
2023-05-25 15:02:00 +08:00
tarsin 498b84df37 Revert "Optimize code"
This reverts commit 6ed3fb29d9.
2023-05-25 15:01:49 +08:00
tarsin 7a4e65259d Revert "Uniform html parse behaviour"
This reverts commit c5c1170383.
2023-05-25 15:01:38 +08:00
tarsin 8cb8dcbe0f Resolve #1067 2023-05-25 14:16:13 +08:00
tarsin da37b3cdff Upgrade compose bom 2023-05-25 13:44:10 +08:00
renovate[bot] 0a1768e481 Update dependency androidx.core:core-ktx to v1.11.0-beta01 2023-05-25 13:41:57 +08:00
renovate[bot] 818d16a32d Update dependency androidx.paging:paging-common-ktx to v3.2.0-alpha06 2023-05-25 13:41:03 +08:00
renovate[bot] e9a5d1cc7c Update dependency androidx.paging:paging-compose to v1.0.0-alpha20 2023-05-25 13:40:19 +08:00
renovate[bot] dac56f3e32 Update dependency androidx.constraintlayout:constraintlayout-compose to v1.1.0-alpha10 2023-05-25 03:55:42 +00:00
renovate[bot] d6cbcdc6cd Update dependency androidx.activity:activity-compose to v1.7.2 2023-05-25 01:29:39 +00:00
tarsin c5c1170383 Uniform html parse behaviour 2023-05-24 20:12:24 +08:00
tarsin 6ed3fb29d9 Optimize code 2023-05-24 19:28:55 +08:00
tarsin 74c6bfa479 Decode cf protected email, step 2, Resolve #1102 2023-05-24 19:19:47 +08:00
tarsin d44981a70c Decode cf protected email, step 1 2023-05-24 18:51:22 +08:00
tarsin 06e411d332 Fixup! Rewrite LauncherUtils 2023-05-23 13:44:32 +08:00
tarsin f419189749 Rewrite LauncherUtils 2023-05-23 13:11:57 +08:00
tarsin aa1e2102fc Fix deprecated coil API 2023-05-22 21:53:59 +08:00
tarsin 306ce9ed21 Always use ImageDecoderDecoder for better performance 2023-05-22 12:33:12 +08:00
renovate[bot] 544fcb2e88 Update dependency io.coil-kt:coil-compose to v2.4.0 2023-05-21 21:44:29 +00:00
tarsin cb01df4071 Revert "Update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-SNAPSHOT"
This reverts commit 38310b7c5a.
2023-05-21 10:42:08 +08:00
renovate[bot] 38310b7c5a Update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-SNAPSHOT 2023-05-20 19:21:10 +00:00
tarsin fcd56ff4e9 Use compose-bom 2023.04.00-alpha04 for reproducible artifact 2023-05-21 00:16:58 +08:00
tarsin 41300a9fd4 Fix okhttp dependency 2023-05-21 00:12:28 +08:00
tarsin 0bd5f9dc31 Downgrade ArrowKt to stable release for reproducible artifacts 2023-05-21 00:11:10 +08:00
tarsin 004ecd0dd3 Resolve #1095 2023-05-21 00:06:56 +08:00
14 changed files with 147 additions and 110 deletions

No files matched your search

+15 -13
View File
@@ -57,8 +57,8 @@ android {
applicationId = "moe.tarsin.ehviewer"
minSdk = 28
targetSdk = 33
versionCode = 180038
versionName = "1.8.8.0-beta"
versionCode = 180039
versionName = "1.8.8.0"
resourceConfigurations.addAll(
listOf(
"zh",
@@ -158,22 +158,22 @@ android {
dependencies {
// https://developer.android.com/jetpack/androidx/releases/activity
implementation("androidx.activity:activity-compose:1.7.1")
implementation("androidx.activity:activity-compose:1.7.2")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05")
implementation("androidx.browser:browser:1.5.0")
implementation("androidx.collection:collection-ktx:1.3.0-alpha04")
// https://developer.android.com/jetpack/androidx/releases/compose-material3
// api(platform("androidx.compose:compose-bom:2023.03.00"))
api(platform("dev.chrisbanes.compose:compose-bom:2023.04.00-SNAPSHOT"))
// api(platform("androidx.compose:compose-bom:2023.05.00"))
api(platform("dev.chrisbanes.compose:compose-bom:2023.04.00-beta01"))
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material3:material3-window-size-class")
implementation("androidx.core:core-ktx:1.11.0-alpha04")
implementation("androidx.core:core-ktx:1.11.0-beta01")
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha09")
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha10")
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
implementation("androidx.drawerlayout:drawerlayout:1.2.0")
// https://developer.android.com/jetpack/androidx/releases/lifecycle
@@ -186,8 +186,8 @@ dependencies {
implementation("androidx.navigation:navigation-compose:$nav_version")
// https://developer.android.com/jetpack/androidx/releases/paging
implementation("androidx.paging:paging-common-ktx:3.2.0-alpha05")
implementation("androidx.paging:paging-compose:1.0.0-alpha19")
implementation("androidx.paging:paging-common-ktx:3.2.0-alpha06")
implementation("androidx.paging:paging-compose:1.0.0-alpha20")
implementation("androidx.preference:preference-ktx:1.2.0")
implementation("androidx.recyclerview:recyclerview:1.3.0")
@@ -207,7 +207,7 @@ dependencies {
implementation("com.google.android.material:material:1.9.0")
// https://square.github.io/okhttp/changelogs/changelog/
implementation("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11")
implementation(platform("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11"))
implementation("com.squareup.okhttp3:okhttp-coroutines")
implementation("com.squareup.okio:okio-jvm:3.3.0")
@@ -219,12 +219,14 @@ dependencies {
implementation("dev.rikka.rikkax.preference:simplemenu-preference:1.0.3")
implementation("dev.rikka.rikkax.material:material-preference:2.0.0")
implementation(platform("io.arrow-kt:arrow-stack:2.0.0-SNAPSHOT"))
implementation("io.arrow-kt:arrow-functions")
implementation(platform("io.arrow-kt:arrow-stack:1.2.0-RC"))
implementation("io.arrow-kt:arrow-fx-coroutines")
// https://coil-kt.github.io/coil/changelog/
implementation("io.coil-kt:coil-compose:2.3.0")
implementation(platform("io.coil-kt:coil-bom:2.4.0"))
implementation("io.coil-kt:coil-compose")
implementation("io.coil-kt:coil-gif")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.5.1")
implementation("org.jsoup:jsoup:1.16.1")
@@ -25,6 +25,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import coil.ImageLoader
import coil.ImageLoaderFactory
import coil.decode.ImageDecoderDecoder
import coil.util.DebugLogger
import com.hippo.ehviewer.client.EhCookieStore
import com.hippo.ehviewer.client.EhDns
@@ -137,7 +138,10 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this).apply {
okHttpClient(nonCacheOkHttpClient)
components { add(MergeInterceptor) }
components {
add { result, options, _ -> ImageDecoderDecoder(result.source, options, false) }
add(MergeInterceptor)
}
diskCache(imageCache)
crossfade(300)
error(R.drawable.image_failed)
@@ -23,6 +23,8 @@ import java.net.Socket
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
private const val EXCEPTIONAL_DOMAIN = "hath.network"
object EhSSLSocketFactory : SSLSocketFactory() {
private val sslSocketFactory = getDefault() as SSLSocketFactory
override fun getDefaultCipherSuites(): Array<String> {
@@ -34,7 +36,7 @@ object EhSSLSocketFactory : SSLSocketFactory() {
}
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val address = s.inetAddress.hostAddress.takeIf { EhDns.isInHosts(host) }
val address = s.inetAddress.hostAddress.takeIf { EhDns.isInHosts(host) || EXCEPTIONAL_DOMAIN in host }
val socket = sslSocketFactory.createSocket(s, address ?: host, port, autoClose) as SSLSocket
val sslSession = socket.session
Log.d(
@@ -1,21 +0,0 @@
package com.hippo.ehviewer.coil
import coil.disk.DiskCache
inline fun DiskCache.edit(key: String, block: DiskCache.Editor.() -> Unit): Boolean {
val editor = edit(key) ?: return false
editor.runCatching {
block(this)
}.onFailure {
editor.abort()
throw it
}.onSuccess {
editor.commit()
}
return true
}
inline fun DiskCache.read(key: String, block: DiskCache.Snapshot.() -> Unit): Boolean {
(get(key) ?: return false).use { block(it) }
return true
}
@@ -0,0 +1,18 @@
package com.hippo.ehviewer.coil
import coil.disk.DiskCache
suspend inline fun DiskCache.edit(key: String, crossinline block: suspend DiskCache.Editor.() -> Unit): Boolean {
val editor = openEditor(key) ?: return false
editor.runCatching {
block(this)
}.onFailure {
editor.abort()
throw it
}.onSuccess {
editor.commit()
}
return true
}
inline fun <T> DiskCache.read(key: String, block: DiskCache.Snapshot.() -> T): T? = openSnapshot(key)?.use(block)
@@ -64,7 +64,7 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
private fun containInCache(index: Int): Boolean {
val key = getImageKey(mGid, index)
return sCache[key]?.use { true } ?: false
return sCache.read(key) { true } ?: false
}
private fun containInDownloadDir(index: Int): Boolean {
@@ -84,11 +84,11 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
it sendTo outFd
}
}
}
}.getOrElse {
true
} ?: false
}.onFailure {
it.printStackTrace()
false
}
}.getOrDefault(false)
}
operator fun contains(index: Int): Boolean {
@@ -199,12 +199,11 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
val key = getImageKey(mGid, index)
// Read from diskCache first
sCache[key]?.use { snapshot ->
sCache.read(key) {
runCatching {
UniFile.fromFile(snapshot.data.toFile())!!.openFileDescriptor("r").use {
UniFile.fromFile(data.toFile())!!.openFileDescriptor("r").use {
it sendTo toFd
}
}.onSuccess {
return true
}.onFailure {
it.printStackTrace()
@@ -235,15 +234,14 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
fun getExtension(index: Int): String? {
val key = getImageKey(mGid, index)
return sCache[key]?.use { it.metadata.toNioPath().readText() }
?: downloadDir?.let { findImageFile(it, index) }
?.name.let { FileUtils.getExtensionFromFilename(it) }
return sCache.read(key) { metadata.toNioPath().readText() }
?: downloadDir?.let { findImageFile(it, index) }?.name.let { FileUtils.getExtensionFromFilename(it) }
}
fun getImageSource(index: Int): CloseableSource? {
if (mode == SpiderQueen.MODE_READ) {
val key = getImageKey(mGid, index)
val snapshot = sCache[key]
val snapshot = sCache.openSnapshot(key)
if (snapshot != null) {
val source = ImageDecoder.createSource(snapshot.data.toFile())
return object : CloseableSource, AutoCloseable by snapshot {
@@ -2,6 +2,8 @@ package com.hippo.ehviewer.spider
import coil.disk.DiskCache
import com.hippo.ehviewer.EhApplication
import com.hippo.ehviewer.coil.edit
import com.hippo.ehviewer.coil.read
import com.hippo.ehviewer.legacy.readLegacySpiderInfo
import com.hippo.unifile.UniFile
import com.hippo.unifile.openInputStream
@@ -10,6 +12,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import moe.tarsin.coroutines.runSuspendCatching
import java.io.File
@Serializable
@@ -35,15 +38,13 @@ fun SpiderInfo.write(file: UniFile) {
}
}
fun SpiderInfo.saveToCache() {
val entry = spiderInfoCache.edit(gid.toString()) ?: return
runCatching {
entry.data.toFile().writeBytes(Cbor.encodeToByteArray(this))
suspend fun SpiderInfo.saveToCache() {
runSuspendCatching {
spiderInfoCache.edit(gid.toString()) {
data.toFile().writeBytes(Cbor.encodeToByteArray(this))
}
}.onFailure {
it.printStackTrace()
entry.abort()
}.onSuccess {
entry.commit()
}
}
@@ -54,14 +55,13 @@ private val spiderInfoCache by lazy {
}
fun readFromCache(gid: Long): SpiderInfo? {
val snapshot = spiderInfoCache[gid.toString()] ?: return null
return runCatching {
snapshot.use {
return Cbor.decodeFromByteArray(it.data.toFile().readBytes())
}
}.onFailure {
it.printStackTrace()
}.getOrNull()
return spiderInfoCache.read(gid.toString()) {
runCatching {
Cbor.decodeFromByteArray<SpiderInfo>(data.toFile().readBytes())
}.onFailure {
it.printStackTrace()
}.getOrNull()
}
}
fun readCompatFromUniFile(file: UniFile): SpiderInfo? {
@@ -49,6 +49,7 @@ import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
@@ -335,8 +336,7 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
it.gid == galleryInfo.gid && it.token == galleryInfo.token
}
}
}
?: readFromCache(galleryInfo.gid)?.takeIf { it.gid == galleryInfo.gid && it.token == galleryInfo.token }
} ?: readFromCache(galleryInfo.gid)?.takeIf { it.gid == galleryInfo.gid && it.token == galleryInfo.token }
}
@Throws(ParseException::class)
@@ -427,7 +427,7 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
private fun writeSpiderInfoToLocal() {
if (!isReady) return
mSpiderDen.downloadDir?.run { createFile(SPIDER_INFO_FILENAME).also { mSpiderInfo.write(it) } }
mSpiderInfo.saveToCache()
runBlocking { mSpiderInfo.saveToCache() }
}
private fun isStateDone(state: Int): Boolean {
@@ -18,9 +18,12 @@ package com.hippo.ehviewer.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
@@ -103,7 +106,10 @@ fun GalleryInfoBottomSheet(
}
}
ModalBottomSheet(onDismissRequest) {
ModalBottomSheet(
onDismissRequest = onDismissRequest,
windowInsets = WindowInsets(0),
) {
check(data.size == keys.size)
val context = LocalContext.current
val navController = LocalNavController.current
@@ -114,23 +120,25 @@ fun GalleryInfoBottomSheet(
Text(text = stringResource(id = R.string.gallery_info), style = MaterialTheme.typography.titleLarge)
}
CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.labelLarge) {
keys.forEachIndexed { index, i ->
val key = stringResource(i)
Row(
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.keyline_margin)).clickable {
if (index == INDEX_PARENT) {
data[index]?.let { navController.navWithUrl(it) }
} else {
context tellClipboardWithToast data[index]
if (index == INDEX_URL) {
// Save it to avoid detect the gallery
Settings.putClipboardTextHashCode(data[index].hashCode())
LazyColumn {
itemsIndexed(keys) { index, i ->
val key = stringResource(i)
Row(
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.keyline_margin)).clickable {
if (index == INDEX_PARENT) {
data[index]?.let { navController.navWithUrl(it) }
} else {
context tellClipboardWithToast data[index]
if (index == INDEX_URL) {
// Save it to avoid detect the gallery
Settings.putClipboardTextHashCode(data[index].hashCode())
}
}
}
}.fillMaxWidth(),
) {
Text(key, modifier = Modifier.width(90.dp).padding(8.dp))
Text(data[index].orEmpty(), modifier = Modifier.padding(8.dp))
}.fillMaxWidth(),
) {
Text(key, modifier = Modifier.width(90.dp).padding(8.dp))
Text(data[index].orEmpty(), modifier = Modifier.padding(8.dp))
}
}
}
}
@@ -73,7 +73,6 @@ import com.hippo.ehviewer.util.addTextToClipboard
import com.hippo.ehviewer.util.getClipboardManager
import com.hippo.ehviewer.util.getParcelableExtraCompat
import com.hippo.ehviewer.util.getUrlFromClipboard
import com.hippo.ehviewer.util.initLauncher
import com.hippo.ehviewer.yorozuya.IOUtils
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.lang.launchUI
@@ -91,10 +90,6 @@ class MainActivity : EhActivity() {
private val connectivityManager by lazy { getSystemService<ConnectivityManager>()!! }
private val availableNetworks = mutableListOf<Network>()
init {
initLauncher()
}
private fun saveImageToTempFile(uri: Uri): File? {
val src = ImageDecoder.createSource(contentResolver, uri)
val bitmap = runCatching {
@@ -54,7 +54,7 @@ class ImageSearchLayout @JvmOverloads constructor(
Mdc3Theme {
val coroutineScope = rememberCoroutineScope()
fun selectImage() = coroutineScope.launch {
pickVisualMedia(ImageOnly)?.let {
context.pickVisualMedia(ImageOnly)?.let {
path = it.toString()
}
}
@@ -59,6 +59,7 @@ import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.coil.imageRequest
import com.hippo.ehviewer.coil.read
import com.hippo.ehviewer.dao.DownloadInfo
import com.hippo.ehviewer.databinding.DrawerListRvBinding
import com.hippo.ehviewer.databinding.ItemDownloadBinding
@@ -912,8 +913,8 @@ class DownloadsScene :
if (!thumbLocation.exists()) {
thumbLocation.ensureFile()
val key = info.thumbKey!!
imageCache[key]?.use {
UniFile.fromFile(it.data.toFile())!!.openFileDescriptor("r").use { src ->
imageCache.read(key) {
UniFile.fromFile(data.toFile())!!.openFileDescriptor("r").use { src ->
thumbLocation.openFileDescriptor("w").use { dst ->
src sendTo dst
}
@@ -116,6 +116,7 @@ import coil.imageLoader
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.google.android.material.snackbar.Snackbar
import com.hippo.ehviewer.EhApplication.Companion.galleryDetailCache
import com.hippo.ehviewer.EhApplication.Companion.imageCache
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
@@ -139,6 +140,7 @@ import com.hippo.ehviewer.client.parser.ParserUtils
import com.hippo.ehviewer.client.parser.TorrentParser
import com.hippo.ehviewer.coil.imageRequest
import com.hippo.ehviewer.coil.justDownload
import com.hippo.ehviewer.coil.read
import com.hippo.ehviewer.dao.DownloadInfo
import com.hippo.ehviewer.dao.Filter
import com.hippo.ehviewer.download.DownloadManager.DownloadInfoListener
@@ -1217,7 +1219,7 @@ class GalleryDetailScene : BaseScene(), DownloadInfoListener {
}
try {
val key = composeBindingGI!!.thumbKey!!
val path = imageLoader(context).diskCache!![key]!!.use { it.data }
val path = imageCache.read(key) { data }
val lub = ListUrlBuilder()
lub.mode = ListUrlBuilder.MODE_IMAGE_SEARCH
lub.imagePath = path.toString()
@@ -1,41 +1,69 @@
package com.hippo.ehviewer.util
import android.content.Context
import android.content.ContextWrapper
import android.content.pm.PackageManager
import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.VisualMediaType
import androidx.core.content.ContextCompat
import arrow.atomic.Atomic
import arrow.atomic.update
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import arrow.atomic.AtomicInt
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.suspendCancellableCoroutine
import rikka.core.util.ContextUtils.requireActivity
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
// Fuck off the silly Android launcher and callback :)
private typealias LauncherAndCallback<K, V> = Atomic<Pair<ActivityResultLauncher<K>, (V) -> Unit>>
private fun <K, V> LauncherAndCallback<K, V>.cleanup() = update { it.copy {} }
private lateinit var requestPermission: LauncherAndCallback<String, Boolean>
private lateinit var pickVisualMedia: LauncherAndCallback<PickVisualMediaRequest, Uri?>
private suspend fun <K, V> LauncherAndCallback<K, V>.await(key: K) = withUIContext {
suspendCancellableCoroutine { cont ->
updateAndGet { prev -> prev.copy { cont.resume(it) } }.first.launch(key)
cont.invokeOnCancellation { cleanup() } // Drop continuation when cancelled
}.apply { cleanup() } // Drop continuation when completed
}
private val atomicInteger = AtomicInt()
fun ComponentActivity.initLauncher() {
requestPermission = Atomic(registerForActivityResult(ActivityResultContracts.RequestPermission()) { requestPermission.get().second(it) } to {})
pickVisualMedia = Atomic(registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { pickVisualMedia.get().second(it) } to {})
private val Context.lifecycle: Lifecycle
get() {
var context: Context? = this
while (true) {
when (context) {
is LifecycleOwner -> return context.lifecycle
!is ContextWrapper -> error("This should never happen!")
else -> context = context.baseContext
}
}
}
private suspend fun <I, O> Context.awaitActivityResult(contract: ActivityResultContract<I, O>, input: I): O {
val key = "activity_rq#${atomicInteger.getAndIncrement()}"
var launcher: ActivityResultLauncher<I>? = null
var observer: LifecycleEventObserver? = null
observer = LifecycleEventObserver { _, event ->
if (Lifecycle.Event.ON_DESTROY == event) {
launcher?.unregister()
if (observer != null) {
lifecycle.removeObserver(observer!!)
}
}
}
lifecycle.addObserver(observer)
return withUIContext {
suspendCoroutine { cont -> // No cancellation support here since we cannot cancel a launched Intent
val activity = requireActivity<ComponentActivity>(this@awaitActivityResult)
launcher = activity.activityResultRegistry.register(key, contract) {
launcher?.unregister()
lifecycle.removeObserver(observer)
cont.resume(it)
}.apply { launch(input) }
}
}
}
suspend fun Context.requestPermission(key: String): Boolean {
if (ContextCompat.checkSelfPermission(this, key) == PackageManager.PERMISSION_GRANTED) return true
return requestPermission.await(key)
return awaitActivityResult(ActivityResultContracts.RequestPermission(), key)
}
suspend fun pickVisualMedia(type: VisualMediaType): Uri? = pickVisualMedia.await(PickVisualMediaRequest.Builder().setMediaType(type).build())
suspend fun Context.pickVisualMedia(type: VisualMediaType): Uri? = awaitActivityResult(ActivityResultContracts.PickVisualMedia(), PickVisualMediaRequest.Builder().setMediaType(type).build())