feat: launch and install from apk intent (#279)

close #90
This commit is contained in:
Js0n 2023-12-11 23:38:30 +08:00 committed by GitHub
parent 5bf494400d
commit 4bf1371155
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 8 deletions

View File

@ -23,6 +23,14 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity> </activity>
<service <service

View File

@ -1,8 +1,10 @@
package org.lsposed.lspatch.ui.page package org.lsposed.lspatch.ui.page
import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -16,7 +18,11 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -27,10 +33,13 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.lsposed.lspatch.R import org.lsposed.lspatch.R
import org.lsposed.lspatch.share.LSPConfig import org.lsposed.lspatch.share.LSPConfig
import org.lsposed.lspatch.ui.component.CenterTopBar import org.lsposed.lspatch.ui.component.CenterTopBar
import org.lsposed.lspatch.ui.page.destinations.ManageScreenDestination
import org.lsposed.lspatch.ui.page.destinations.NewPatchScreenDestination
import org.lsposed.lspatch.ui.util.HtmlText import org.lsposed.lspatch.ui.util.HtmlText
import org.lsposed.lspatch.ui.util.LocalSnackbarHost import org.lsposed.lspatch.ui.util.LocalSnackbarHost
import org.lsposed.lspatch.util.ShizukuApi import org.lsposed.lspatch.util.ShizukuApi
@ -40,7 +49,27 @@ import rikka.shizuku.Shizuku
@RootNavGraph(start = true) @RootNavGraph(start = true)
@Destination @Destination
@Composable @Composable
fun HomeScreen() { fun HomeScreen(navigator: DestinationsNavigator) {
// Install from intent
var isIntentLaunched by rememberSaveable { mutableStateOf(false) }
val activity = LocalContext.current as Activity
val intent = activity.intent
LaunchedEffect(Unit) {
if (!isIntentLaunched && intent.action == Intent.ACTION_VIEW && intent.hasCategory(Intent.CATEGORY_DEFAULT) && intent.type == "application/vnd.android.package-archive") {
isIntentLaunched = true
val uri = intent.data
if (uri != null) {
navigator.navigate(ManageScreenDestination)
navigator.navigate(
NewPatchScreenDestination(
id = ACTION_INTENT_INSTALL,
data = uri
)
)
}
}
}
Scaffold( Scaffold(
topBar = { CenterTopBar(stringResource(R.string.app_name)) } topBar = { CenterTopBar(stringResource(R.string.app_name)) }
) { innerPadding -> ) { innerPadding ->

View File

@ -4,6 +4,7 @@ import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller
import android.net.Uri
import android.util.Log import android.util.Log
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
@ -58,13 +59,18 @@ import org.lsposed.lspatch.util.ShizukuApi
private const val TAG = "NewPatchPage" private const val TAG = "NewPatchPage"
const val ACTION_STORAGE = 0
const val ACTION_APPLIST = 1
const val ACTION_INTENT_INSTALL = 2
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Destination @Destination
@Composable @Composable
fun NewPatchScreen( fun NewPatchScreen(
navigator: DestinationsNavigator, navigator: DestinationsNavigator,
resultRecipient: ResultRecipient<SelectAppsScreenDestination, SelectAppsResult>, resultRecipient: ResultRecipient<SelectAppsScreenDestination, SelectAppsResult>,
from: String id: Int,
data: Uri? = null
) { ) {
val viewModel = viewModel<NewPatchViewModel>() val viewModel = viewModel<NewPatchViewModel>()
val snackbarHost = LocalSnackbarHost.current val snackbarHost = LocalSnackbarHost.current
@ -116,11 +122,34 @@ fun NewPatchScreen(
PatchState.INIT -> { PatchState.INIT -> {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
LSPPackageManager.cleanTmpApkDir() LSPPackageManager.cleanTmpApkDir()
when (from) { when (id) {
"storage" -> storageLauncher.launch(arrayOf("application/vnd.android.package-archive")) ACTION_STORAGE -> {
"applist" -> navigator.navigate(SelectAppsScreenDestination(false)) storageLauncher.launch(arrayOf("application/vnd.android.package-archive"))
viewModel.dispatch(ViewAction.DoneInit)
}
ACTION_APPLIST -> {
navigator.navigate(SelectAppsScreenDestination(false))
viewModel.dispatch(ViewAction.DoneInit)
}
ACTION_INTENT_INSTALL -> {
runBlocking {
data?.let { uri ->
LSPPackageManager.getAppInfoFromApks(listOf(uri)).onSuccess {
viewModel.dispatch(ViewAction.ConfigurePatch(it.first()))
}.onFailure {
lspApp.globalScope.launch {
snackbarHost.showSnackbar(
it.message ?: errorUnknown
)
}
navigator.navigateUp()
}
}
}
}
} }
viewModel.dispatch(ViewAction.DoneInit)
} }
} }
PatchState.SELECTING -> { PatchState.SELECTING -> {

View File

@ -48,6 +48,8 @@ import org.lsposed.lspatch.share.LSPConfig
import org.lsposed.lspatch.ui.component.AnywhereDropdown import org.lsposed.lspatch.ui.component.AnywhereDropdown
import org.lsposed.lspatch.ui.component.AppItem import org.lsposed.lspatch.ui.component.AppItem
import org.lsposed.lspatch.ui.component.LoadingDialog import org.lsposed.lspatch.ui.component.LoadingDialog
import org.lsposed.lspatch.ui.page.ACTION_APPLIST
import org.lsposed.lspatch.ui.page.ACTION_STORAGE
import org.lsposed.lspatch.ui.page.SelectAppsResult import org.lsposed.lspatch.ui.page.SelectAppsResult
import org.lsposed.lspatch.ui.page.destinations.NewPatchScreenDestination import org.lsposed.lspatch.ui.page.destinations.NewPatchScreenDestination
import org.lsposed.lspatch.ui.page.destinations.SelectAppsScreenDestination import org.lsposed.lspatch.ui.page.destinations.SelectAppsScreenDestination
@ -329,7 +331,7 @@ fun AppManageFab(navigator: DestinationsNavigator) {
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.secondary), colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.secondary),
onClick = { onClick = {
navigator.navigate(NewPatchScreenDestination("storage")) navigator.navigate(NewPatchScreenDestination(id = ACTION_STORAGE))
showNewPatchDialog = false showNewPatchDialog = false
} }
) { ) {
@ -343,7 +345,7 @@ fun AppManageFab(navigator: DestinationsNavigator) {
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.secondary), colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.secondary),
onClick = { onClick = {
navigator.navigate(NewPatchScreenDestination("applist")) navigator.navigate(NewPatchScreenDestination(id = ACTION_APPLIST))
showNewPatchDialog = false showNewPatchDialog = false
} }
) { ) {