manager: support offline patch
This commit is contained in:
@@ -137,7 +137,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
|
||||
@Parcelize
|
||||
sealed class FlashIt : Parcelable {
|
||||
data class FlashBoot(val bootUri: Uri? = null, val koUri: Uri, val ota: Boolean) : FlashIt()
|
||||
data class FlashBoot(val bootUri: Uri? = null, val ota: Boolean) : FlashIt()
|
||||
|
||||
data class FlashModule(val uri: Uri) : FlashIt()
|
||||
}
|
||||
@@ -150,7 +150,6 @@ fun flashIt(
|
||||
when (flashIt) {
|
||||
is FlashIt.FlashBoot -> installBoot(
|
||||
flashIt.bootUri,
|
||||
flashIt.koUri,
|
||||
flashIt.ota,
|
||||
onFinish,
|
||||
onStdout,
|
||||
|
||||
@@ -57,55 +57,17 @@ import me.weishu.kernelsu.ui.util.rootAvailable
|
||||
@Destination
|
||||
@Composable
|
||||
fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val loadingDialog = rememberLoadingDialog()
|
||||
val context = LocalContext.current
|
||||
var installMethod by remember {
|
||||
mutableStateOf<InstallMethod?>(null)
|
||||
}
|
||||
|
||||
val onFileDownloaded = { uri: Uri ->
|
||||
|
||||
installMethod?.let {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
when (it) {
|
||||
InstallMethod.DirectInstall -> {
|
||||
navigator.navigate(
|
||||
FlashScreenDestination(
|
||||
FlashIt.FlashBoot(
|
||||
null,
|
||||
uri,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
InstallMethod.DirectInstallToInactiveSlot -> {
|
||||
navigator.navigate(
|
||||
FlashScreenDestination(
|
||||
FlashIt.FlashBoot(
|
||||
null,
|
||||
uri,
|
||||
true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is InstallMethod.SelectFile -> {
|
||||
navigator.navigate(
|
||||
FlashScreenDestination(
|
||||
FlashIt.FlashBoot(
|
||||
it.uri,
|
||||
uri,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val onClickInstall = {
|
||||
installMethod?.let { method ->
|
||||
val flashIt = FlashIt.FlashBoot(
|
||||
if (method is InstallMethod.SelectFile) method.uri else null,
|
||||
method is InstallMethod.DirectInstallToInactiveSlot
|
||||
)
|
||||
navigator.navigate(FlashScreenDestination(flashIt))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,45 +86,11 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
|
||||
DownloadListener(context = context) { uri ->
|
||||
onFileDownloaded(uri)
|
||||
loadingDialog.hide()
|
||||
}
|
||||
|
||||
val failedMessage = stringResource(id = R.string.failed_to_fetch_lkm_url)
|
||||
val downloadingMessage = stringResource(id = R.string.downloading)
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = installMethod != null,
|
||||
onClick = {
|
||||
loadingDialog.showLoading()
|
||||
scope.launch(Dispatchers.IO) {
|
||||
getLKMUrl().onFailure { throwable ->
|
||||
loadingDialog.hide()
|
||||
scope.launch(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
failedMessage.format(throwable.message),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}.onSuccess { result ->
|
||||
download(
|
||||
context = context,
|
||||
url = result.second,
|
||||
fileName = result.first,
|
||||
description = downloadingMessage.format(
|
||||
result.first
|
||||
),
|
||||
onDownloaded = { uri ->
|
||||
onFileDownloaded(uri)
|
||||
loadingDialog.hide()
|
||||
},
|
||||
onDownloading = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
onClickInstall()
|
||||
}) {
|
||||
Text(
|
||||
stringResource(id = R.string.install_next),
|
||||
|
||||
@@ -143,7 +143,6 @@ fun installModule(
|
||||
|
||||
fun installBoot(
|
||||
bootUri: Uri?,
|
||||
lkmUri: Uri,
|
||||
ota: Boolean,
|
||||
onFinish: (Boolean) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
@@ -151,73 +150,60 @@ fun installBoot(
|
||||
): Boolean {
|
||||
val resolver = ksuApp.contentResolver
|
||||
|
||||
with(resolver.openInputStream(lkmUri)) {
|
||||
val lkmFile = File(ksuApp.cacheDir, "kernelsu.ko")
|
||||
lkmFile.outputStream().use { output ->
|
||||
this?.copyTo(output)
|
||||
}
|
||||
|
||||
if (!lkmFile.exists()) {
|
||||
onStdout("- kernelsu.ko not found")
|
||||
onFinish(false)
|
||||
return false
|
||||
}
|
||||
|
||||
val bootFile = bootUri?.let { uri ->
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val bootFile = File(ksuApp.cacheDir, "boot.img")
|
||||
bootFile.outputStream().use { output ->
|
||||
this?.copyTo(output)
|
||||
}
|
||||
|
||||
bootFile
|
||||
val bootFile = bootUri?.let { uri ->
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val bootFile = File(ksuApp.cacheDir, "boot.img")
|
||||
bootFile.outputStream().use { output ->
|
||||
this?.copyTo(output)
|
||||
}
|
||||
|
||||
bootFile
|
||||
}
|
||||
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
var cmd = "boot-patch -m ${lkmFile.absolutePath} --magiskboot ${magiskboot.absolutePath}"
|
||||
|
||||
cmd += if (bootFile == null) {
|
||||
// no boot.img, use -f to force install
|
||||
" -f"
|
||||
} else {
|
||||
" -b ${bootFile.absolutePath}"
|
||||
}
|
||||
|
||||
if (ota) {
|
||||
cmd += " -u"
|
||||
}
|
||||
|
||||
// output dir
|
||||
val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
cmd += " -o $downloadsDir"
|
||||
|
||||
val shell = createRootShell()
|
||||
|
||||
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStdout(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val stderrCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStderr(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} $cmd").to(stdoutCallback, stderrCallback)
|
||||
.exec()
|
||||
Log.i("KernelSU", "install boot $lkmUri result: ${result.isSuccess}")
|
||||
|
||||
lkmFile.delete()
|
||||
bootFile?.delete()
|
||||
|
||||
// if boot uri is empty, it is direct install, when success, we should show reboot button
|
||||
onFinish(bootUri == null && result.isSuccess)
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
var cmd = "boot-patch --magiskboot ${magiskboot.absolutePath}"
|
||||
|
||||
cmd += if (bootFile == null) {
|
||||
// no boot.img, use -f to force install
|
||||
" -f"
|
||||
} else {
|
||||
" -b ${bootFile.absolutePath}"
|
||||
}
|
||||
|
||||
if (ota) {
|
||||
cmd += " -u"
|
||||
}
|
||||
|
||||
// output dir
|
||||
val downloadsDir =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
cmd += " -o $downloadsDir"
|
||||
|
||||
val shell = createRootShell()
|
||||
|
||||
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStdout(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val stderrCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStderr(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} $cmd").to(stdoutCallback, stderrCallback)
|
||||
.exec()
|
||||
Log.i("KernelSU", "install boot result: ${result.isSuccess}")
|
||||
|
||||
bootFile?.delete()
|
||||
|
||||
// if boot uri is empty, it is direct install, when success, we should show reboot button
|
||||
onFinish(bootUri == null && result.isSuccess)
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun reboot(reason: String = "") {
|
||||
@@ -250,7 +236,8 @@ fun isInitBoot(): Boolean {
|
||||
return file.exists()
|
||||
}
|
||||
// https://source.android.com/docs/core/architecture/partitions/generic-boot
|
||||
return ShellUtils.fastCmd(shell, "getprop ro.product.first_api_level").trim().toInt() >= Build.VERSION_CODES.TIRAMISU
|
||||
return ShellUtils.fastCmd(shell, "getprop ro.product.first_api_level").trim()
|
||||
.toInt() >= Build.VERSION_CODES.TIRAMISU
|
||||
}
|
||||
|
||||
fun overlayFsAvailable(): Boolean {
|
||||
|
||||
@@ -109,8 +109,6 @@
|
||||
<string name="enable_web_debugging">تمكين تصحيح أخطاء WebView</string>
|
||||
<string name="enable_web_debugging_summary">يمكن استخدامه لتصحيح أخطاء WebUI، يرجى تمكينه فقط عند الحاجة.</string>
|
||||
<string name="install_next">التالي</string>
|
||||
<string name="failed_to_fetch_lkm_url">فشل جلب رابط LKM: %1$s</string>
|
||||
<string name="downloading">تحميل %1$s</string>
|
||||
<string name="select_file">اختيار ملف</string>
|
||||
<string name="direct_install">تثبيت مباشر (موصى به)</string>
|
||||
<string name="install_inactive_slot">التثبيت على فتحة غير نشطة (بعد OTA)</string>
|
||||
|
||||
@@ -116,6 +116,4 @@
|
||||
\nN\'utilisez cette option qu\'une fois la mise à jour OTA terminée.
|
||||
\nContinuer ?</string>
|
||||
<string name="install_next">Suivant</string>
|
||||
<string name="downloading">Téléchargement de %1$s</string>
|
||||
<string name="failed_to_fetch_lkm_url">Échec de la récupération de l\'URL du LKM : %1$s</string>
|
||||
</resources>
|
||||
@@ -115,7 +115,5 @@
|
||||
<string name="install_inactive_slot_warning">Seu dispositivo será FORÇADO a inicializar no slot inativo atual após uma reinicialização!
|
||||
\nSó use esta opção após a conclusão do OTA.
|
||||
\nDeseja continuar?</string>
|
||||
<string name="downloading">Baixando %1$s</string>
|
||||
<string name="install_next">Próximo</string>
|
||||
<string name="failed_to_fetch_lkm_url">Falha ao buscar a URL do LKM: %1$s</string>
|
||||
</resources>
|
||||
@@ -112,8 +112,6 @@
|
||||
<string name="open">Открыть</string>
|
||||
<string name="enable_web_debugging">Включить отладку WebView</string>
|
||||
<string name="enable_web_debugging_summary">Используется для отладки веб-интерфейса. Пожалуйста, включайте только при необходимости.</string>
|
||||
<string name="downloading">Загрузка %1$s</string>
|
||||
<string name="failed_to_fetch_lkm_url">Не удалось получить LKM по адресу: %1$s</string>
|
||||
<string name="direct_install">Прямая установка (Рекомендуется)</string>
|
||||
<string name="install_inactive_slot">Установка в неактивный слот (После OTA)</string>
|
||||
<string name="install_next">Далее</string>
|
||||
|
||||
@@ -117,6 +117,4 @@
|
||||
\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.
|
||||
\nDevam edilsin mi?</string>
|
||||
<string name="install_next">Sonraki</string>
|
||||
<string name="failed_to_fetch_lkm_url">LKM URL\'si getirilemedi: %1$s</string>
|
||||
<string name="downloading">%1$s indiriliyor</string>
|
||||
</resources>
|
||||
@@ -113,7 +113,5 @@
|
||||
<string name="install_inactive_slot">安装到未使用的槽位(OTA 后)</string>
|
||||
<string name="install_inactive_slot_warning">将在重启后强制切换到另一个槽位!\n注意只能在 OTA 更新完成后的重启之前使用。\n确认?</string>
|
||||
<string name="install_next">下一步</string>
|
||||
<string name="failed_to_fetch_lkm_url">获取 LKM 链接失败:%1$s</string>
|
||||
<string name="downloading">正在下载:%1$s</string>
|
||||
<string name="select_file_tip">建议选择 %1$s 分区镜像</string>
|
||||
</resources>
|
||||
@@ -115,6 +115,4 @@
|
||||
\n請問是否繼續?</string>
|
||||
<string name="direct_install">直接安裝(建議)</string>
|
||||
<string name="install_next">下一步</string>
|
||||
<string name="failed_to_fetch_lkm_url">取得 LKM 下載鏈接失敗:%1$s</string>
|
||||
<string name="downloading">正在下載:%1$s</string>
|
||||
</resources>
|
||||
@@ -115,7 +115,5 @@
|
||||
<string name="install_inactive_slot">Install to Inactive Slot (After OTA)</string>
|
||||
<string name="install_inactive_slot_warning">Your device will be **FORCED** to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue?</string>
|
||||
<string name="install_next">Next</string>
|
||||
<string name="failed_to_fetch_lkm_url">Failed to fetch LKM url: %1$s</string>
|
||||
<string name="downloading">Downloading %1$s</string>
|
||||
<string name="select_file_tip">%1$s partition image is recommended</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user