diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 90597b3ea..66c9f09ec 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,6 +13,7 @@
+
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt
index ab795d277..b7373045e 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt
+++ b/app/src/main/java/com/hippo/ehviewer/ui/settings/AboutScreen.kt
@@ -18,6 +18,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.text.parseAsHtml
import com.hippo.ehviewer.AppConfig
@@ -28,8 +29,10 @@ import com.hippo.ehviewer.client.execute
import com.hippo.ehviewer.ui.LICENSE_SCREEN
import com.hippo.ehviewer.ui.LocalNavController
import com.hippo.ehviewer.ui.tools.toAnnotatedString
+import com.hippo.ehviewer.util.installPackage
import com.hippo.ehviewer.util.iter
import com.hippo.ehviewer.yorozuya.FileUtils
+import eu.kanade.tachiyomi.util.lang.withUIContext
import io.ktor.util.encodeBase64
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -52,6 +55,7 @@ private fun author() = stringResource(R.string.settings_about_author_summary).re
@Composable
fun AboutScreen() {
+ val context = LocalContext.current
val navController = LocalNavController.current
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val snackbarHostState = remember { SnackbarHostState() }
@@ -117,8 +121,8 @@ fun AboutScreen() {
}
}?.getString("archive_download_url")
if (archiveUrl != null) {
+ val file = FileUtils.createTempFile(AppConfig.tempDir, "apk")!!
ghRequest(archiveUrl).execute {
- val file = FileUtils.createTempFile(AppConfig.tempDir, "apk")!!
ZipInputStream(body.byteStream()).use { zip ->
zip.nextEntry
file.outputStream().use {
@@ -126,6 +130,7 @@ fun AboutScreen() {
}
}
}
+ withUIContext { context.installPackage(file) }
} else {
launchSnackBar("CI is still running, please wait")
}
diff --git a/app/src/main/java/com/hippo/ehviewer/util/LauncherUtils.kt b/app/src/main/java/com/hippo/ehviewer/util/LauncherUtils.kt
index 36af7736e..3d935493b 100644
--- a/app/src/main/java/com/hippo/ehviewer/util/LauncherUtils.kt
+++ b/app/src/main/java/com/hippo/ehviewer/util/LauncherUtils.kt
@@ -1,9 +1,12 @@
package com.hippo.ehviewer.util
+import android.Manifest
import android.content.Context
import android.content.ContextWrapper
+import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
+import android.provider.Settings
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
@@ -11,12 +14,14 @@ 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 androidx.core.content.FileProvider
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import arrow.atomic.AtomicInt
import eu.kanade.tachiyomi.util.lang.withUIContext
import rikka.core.util.ContextUtils.requireActivity
+import java.io.File
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -67,3 +72,34 @@ suspend fun Context.requestPermission(key: String): Boolean {
}
suspend fun Context.pickVisualMedia(type: VisualMediaType): Uri? = awaitActivityResult(ActivityResultContracts.PickVisualMedia(), PickVisualMediaRequest.Builder().setMediaType(type).build())
+
+suspend fun Context.requestInstallPermission(): Boolean {
+ if (packageManager.canRequestPackageInstalls()) return true
+ val granted = requestPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES)
+ if (!granted) {
+ awaitActivityResult(
+ ActivityResultContracts.StartActivityForResult(),
+ Intent(
+ Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
+ Uri.parse("package:$packageName"),
+ ),
+ )
+ requestPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES)
+ }
+ return packageManager.canRequestPackageInstalls()
+}
+
+suspend fun Context.installPackage(file: File) {
+ val canInstall = requestInstallPermission()
+ if (canInstall) {
+ val contentUri = FileProvider.getUriForFile(this, "$packageName.fileprovider", file)
+ val intent = Intent(Intent.ACTION_VIEW).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ setDataAndType(contentUri, "application/vnd.android.package-archive")
+ }
+ if (packageManager.queryIntentActivities(intent, 0).size > 0) {
+ startActivity(intent)
+ }
+ }
+}
diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml
index 18e5954aa..64e322854 100644
--- a/app/src/main/res/xml/filepaths.xml
+++ b/app/src/main/res/xml/filepaths.xml
@@ -20,6 +20,10 @@
name="temp"
path="." />
+
+