From 0b95c340d61e35019113dec794f55738fc206a82 Mon Sep 17 00:00:00 2001 From: tarsin Date: Wed, 5 Jul 2023 10:47:36 +0800 Subject: [PATCH] Support install CI apk --- app/src/main/AndroidManifest.xml | 1 + .../hippo/ehviewer/ui/settings/AboutScreen.kt | 7 +++- .../com/hippo/ehviewer/util/LauncherUtils.kt | 36 +++++++++++++++++++ app/src/main/res/xml/filepaths.xml | 4 +++ 4 files changed, 47 insertions(+), 1 deletion(-) 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="." /> + +