manager: Fix save log (#2170)
https://github.com/user-attachments/assets/69467e00-0af9-4d46-add8-e24e767462bd
Use `ContextCompat` in `DownloadListener`
Bump ksp to `2.0.21-1.0.26`
Misc changes (See the
[commit](1fb49d918a)
directly)
This commit is contained in:
@@ -326,7 +326,8 @@ private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) {
|
|||||||
touchPoint = it
|
touchPoint = it
|
||||||
expanded = true
|
expanded = true
|
||||||
}
|
}
|
||||||
}) {
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
content()
|
content()
|
||||||
|
|
||||||
|
|||||||
@@ -356,6 +356,6 @@ private fun TopBar(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun SelectInstall_Preview() {
|
fun SelectInstallPreview() {
|
||||||
InstallScreen(EmptyDestinationsNavigator)
|
InstallScreen(EmptyDestinationsNavigator)
|
||||||
}
|
}
|
||||||
@@ -131,9 +131,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
if (hideInstallButton) {
|
if (!hideInstallButton) {
|
||||||
/* Empty */
|
|
||||||
} else {
|
|
||||||
val moduleInstall = stringResource(id = R.string.module_install)
|
val moduleInstall = stringResource(id = R.string.module_install)
|
||||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.StartActivityForResult()
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
@@ -162,7 +160,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
icon = { Icon(Icons.Filled.Add, moduleInstall) },
|
icon = { Icon(Icons.Filled.Add, moduleInstall) },
|
||||||
text = { Text(text = moduleInstall) },
|
text = { Text(text = moduleInstall) },
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -35,6 +37,7 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
@@ -83,9 +86,11 @@ import me.weishu.kernelsu.ui.component.SwitchItem
|
|||||||
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||||
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||||
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
||||||
|
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||||
import me.weishu.kernelsu.ui.util.getFileNameFromUri
|
|
||||||
import me.weishu.kernelsu.ui.util.shrinkModules
|
import me.weishu.kernelsu.ui.util.shrinkModules
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -96,6 +101,7 @@ import me.weishu.kernelsu.ui.util.shrinkModules
|
|||||||
@Composable
|
@Composable
|
||||||
fun SettingScreen(navigator: DestinationsNavigator) {
|
fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
val snackBarHost = LocalSnackbarHost.current
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -106,6 +112,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
snackbarHost = { SnackbarHost(snackBarHost) },
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
val aboutDialog = rememberCustomDialog {
|
val aboutDialog = rememberCustomDialog {
|
||||||
@@ -124,6 +131,22 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val exportBugreportLauncher = rememberLauncherForActivityResult(
|
||||||
|
ActivityResultContracts.CreateDocument("application/gzip")
|
||||||
|
) { uri: Uri? ->
|
||||||
|
if (uri == null) return@rememberLauncherForActivityResult
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
loadingDialog.show()
|
||||||
|
context.contentResolver.openOutputStream(uri)?.use { output ->
|
||||||
|
getBugreportFile(context).inputStream().use {
|
||||||
|
it.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadingDialog.hide()
|
||||||
|
snackBarHost.showSnackbar(context.getString(R.string.log_saved))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val profileTemplate = stringResource(id = R.string.settings_profile_template)
|
val profileTemplate = stringResource(id = R.string.settings_profile_template)
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) },
|
leadingContent = { Icon(Icons.Filled.Fence, profileTemplate) },
|
||||||
@@ -208,35 +231,10 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
scope.launch {
|
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
|
||||||
val bugreport = loadingDialog.withLoading {
|
val current = LocalDateTime.now().format(formatter)
|
||||||
withContext(Dispatchers.IO) {
|
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
|
||||||
getBugreportFile(context)
|
showBottomsheet = false
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val uri: Uri =
|
|
||||||
FileProvider.getUriForFile(
|
|
||||||
context,
|
|
||||||
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
|
||||||
bugreport
|
|
||||||
)
|
|
||||||
val filename = getFileNameFromUri(context, uri)
|
|
||||||
val savefile =
|
|
||||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
type = "application/zip"
|
|
||||||
putExtra(Intent.EXTRA_STREAM, uri)
|
|
||||||
putExtra(Intent.EXTRA_TITLE, filename)
|
|
||||||
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
||||||
}
|
|
||||||
context.startActivity(
|
|
||||||
Intent.createChooser(
|
|
||||||
savefile,
|
|
||||||
context.getString(R.string.save_log)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -256,7 +254,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Box {
|
Box {
|
||||||
Column(
|
Column(
|
||||||
@@ -277,10 +274,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
bugreport
|
bugreport
|
||||||
)
|
)
|
||||||
|
|
||||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
shareIntent.setDataAndType(uri, "application/zip")
|
setDataAndType(uri, "application/gzip")
|
||||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
|
||||||
context.startActivity(
|
context.startActivity(
|
||||||
Intent.createChooser(
|
Intent.createChooser(
|
||||||
@@ -305,16 +303,12 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
trim = LineHeightStyle.Trim.None
|
trim = LineHeightStyle.Trim.None
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
||||||
@@ -329,8 +323,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
headlineContent = { Text(shrink) },
|
headlineContent = { Text(shrink) },
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val result =
|
val result = shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
||||||
shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
|
||||||
if (result == ConfirmResult.Confirmed) {
|
if (result == ConfirmResult.Confirmed) {
|
||||||
loadingDialog.withLoading {
|
loadingDialog.withLoading {
|
||||||
shrinkModules()
|
shrinkModules()
|
||||||
@@ -340,8 +333,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
val lkmMode =
|
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||||
Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
|
||||||
if (lkmMode) {
|
if (lkmMode) {
|
||||||
UninstallItem(navigator) {
|
UninstallItem(navigator) {
|
||||||
loadingDialog.withLoading(it)
|
loadingDialog.withLoading(it)
|
||||||
@@ -353,7 +345,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Filled.ContactPage,
|
Icons.Filled.ContactPage,
|
||||||
stringResource(id = R.string.about)
|
about
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
headlineContent = { Text(about) },
|
headlineContent = { Text(about) },
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,8 +26,7 @@ fun download(
|
|||||||
onDownloaded: (Uri) -> Unit = {},
|
onDownloaded: (Uri) -> Unit = {},
|
||||||
onDownloading: () -> Unit = {}
|
onDownloading: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val downloadManager =
|
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||||
context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
|
||||||
|
|
||||||
val query = DownloadManager.Query()
|
val query = DownloadManager.Query()
|
||||||
query.setFilterByStatus(DownloadManager.STATUS_RUNNING or DownloadManager.STATUS_PAUSED or DownloadManager.STATUS_PENDING)
|
query.setFilterByStatus(DownloadManager.STATUS_RUNNING or DownloadManager.STATUS_PAUSED or DownloadManager.STATUS_PENDING)
|
||||||
@@ -130,18 +129,12 @@ fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
ContextCompat.registerReceiver(
|
||||||
context.registerReceiver(
|
context,
|
||||||
receiver,
|
receiver,
|
||||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||||
Context.RECEIVER_EXPORTED
|
ContextCompat.RECEIVER_EXPORTED
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
context.registerReceiver(
|
|
||||||
receiver,
|
|
||||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onDispose {
|
onDispose {
|
||||||
context.unregisterReceiver(receiver)
|
context.unregisterReceiver(receiver)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,10 +51,10 @@ inline fun <T> withNewRootShell(
|
|||||||
return createRootShell(globalMnt).use(block)
|
return createRootShell(globalMnt).use(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFileNameFromUri(context: Context, uri: Uri): String? {
|
fun Uri.getFileName(context: Context): String? {
|
||||||
var fileName: String? = null
|
var fileName: String? = null
|
||||||
val contentResolver: ContentResolver = context.contentResolver
|
val contentResolver: ContentResolver = context.contentResolver
|
||||||
val cursor: Cursor? = contentResolver.query(uri, null, null, null, null)
|
val cursor: Cursor? = contentResolver.query(this, null, null, null, null)
|
||||||
cursor?.use {
|
cursor?.use {
|
||||||
if (it.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
fileName = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
|
fileName = it.getString(it.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
|
||||||
|
|||||||
@@ -130,4 +130,5 @@
|
|||||||
<string name="flash_failed">刷写失败</string>
|
<string name="flash_failed">刷写失败</string>
|
||||||
<string name="selected_lkm">选择的 LKM :%s</string>
|
<string name="selected_lkm">选择的 LKM :%s</string>
|
||||||
<string name="save_log">保存日志</string>
|
<string name="save_log">保存日志</string>
|
||||||
|
<string name="log_saved">日志已保存</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -132,4 +132,5 @@
|
|||||||
<string name="flash_failed">Flash failed</string>
|
<string name="flash_failed">Flash failed</string>
|
||||||
<string name="selected_lkm">Selected LKM: %s</string>
|
<string name="selected_lkm">Selected LKM: %s</string>
|
||||||
<string name="save_log">Save logs</string>
|
<string name="save_log">Save logs</string>
|
||||||
|
<string name="log_saved">Logs saved</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.7.1"
|
agp = "8.7.1"
|
||||||
kotlin = "2.0.21"
|
kotlin = "2.0.21"
|
||||||
ksp = "2.0.21-1.0.25"
|
ksp = "2.0.21-1.0.26"
|
||||||
compose-bom = "2024.10.00"
|
compose-bom = "2024.10.00"
|
||||||
lifecycle = "2.8.6"
|
lifecycle = "2.8.6"
|
||||||
navigation = "2.8.3"
|
navigation = "2.8.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user