manager: support offline patch
This commit is contained in:
@@ -137,7 +137,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
sealed class FlashIt : Parcelable {
|
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()
|
data class FlashModule(val uri: Uri) : FlashIt()
|
||||||
}
|
}
|
||||||
@@ -150,7 +150,6 @@ fun flashIt(
|
|||||||
when (flashIt) {
|
when (flashIt) {
|
||||||
is FlashIt.FlashBoot -> installBoot(
|
is FlashIt.FlashBoot -> installBoot(
|
||||||
flashIt.bootUri,
|
flashIt.bootUri,
|
||||||
flashIt.koUri,
|
|
||||||
flashIt.ota,
|
flashIt.ota,
|
||||||
onFinish,
|
onFinish,
|
||||||
onStdout,
|
onStdout,
|
||||||
|
|||||||
@@ -57,55 +57,17 @@ import me.weishu.kernelsu.ui.util.rootAvailable
|
|||||||
@Destination
|
@Destination
|
||||||
@Composable
|
@Composable
|
||||||
fun InstallScreen(navigator: DestinationsNavigator) {
|
fun InstallScreen(navigator: DestinationsNavigator) {
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val loadingDialog = rememberLoadingDialog()
|
|
||||||
val context = LocalContext.current
|
|
||||||
var installMethod by remember {
|
var installMethod by remember {
|
||||||
mutableStateOf<InstallMethod?>(null)
|
mutableStateOf<InstallMethod?>(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val onFileDownloaded = { uri: Uri ->
|
val onClickInstall = {
|
||||||
|
installMethod?.let { method ->
|
||||||
installMethod?.let {
|
val flashIt = FlashIt.FlashBoot(
|
||||||
scope.launch(Dispatchers.Main) {
|
if (method is InstallMethod.SelectFile) method.uri else null,
|
||||||
when (it) {
|
method is InstallMethod.DirectInstallToInactiveSlot
|
||||||
InstallMethod.DirectInstall -> {
|
)
|
||||||
navigator.navigate(
|
navigator.navigate(FlashScreenDestination(flashIt))
|
||||||
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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,45 +86,11 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.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(
|
Button(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = installMethod != null,
|
enabled = installMethod != null,
|
||||||
onClick = {
|
onClick = {
|
||||||
loadingDialog.showLoading()
|
onClickInstall()
|
||||||
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 = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}) {
|
}) {
|
||||||
Text(
|
Text(
|
||||||
stringResource(id = R.string.install_next),
|
stringResource(id = R.string.install_next),
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ fun installModule(
|
|||||||
|
|
||||||
fun installBoot(
|
fun installBoot(
|
||||||
bootUri: Uri?,
|
bootUri: Uri?,
|
||||||
lkmUri: Uri,
|
|
||||||
ota: Boolean,
|
ota: Boolean,
|
||||||
onFinish: (Boolean) -> Unit,
|
onFinish: (Boolean) -> Unit,
|
||||||
onStdout: (String) -> Unit,
|
onStdout: (String) -> Unit,
|
||||||
@@ -151,73 +150,60 @@ fun installBoot(
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val resolver = ksuApp.contentResolver
|
val resolver = ksuApp.contentResolver
|
||||||
|
|
||||||
with(resolver.openInputStream(lkmUri)) {
|
val bootFile = bootUri?.let { uri ->
|
||||||
val lkmFile = File(ksuApp.cacheDir, "kernelsu.ko")
|
with(resolver.openInputStream(uri)) {
|
||||||
lkmFile.outputStream().use { output ->
|
val bootFile = File(ksuApp.cacheDir, "boot.img")
|
||||||
this?.copyTo(output)
|
bootFile.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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = "") {
|
fun reboot(reason: String = "") {
|
||||||
@@ -250,7 +236,8 @@ fun isInitBoot(): Boolean {
|
|||||||
return file.exists()
|
return file.exists()
|
||||||
}
|
}
|
||||||
// https://source.android.com/docs/core/architecture/partitions/generic-boot
|
// 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 {
|
fun overlayFsAvailable(): Boolean {
|
||||||
|
|||||||
@@ -109,8 +109,6 @@
|
|||||||
<string name="enable_web_debugging">تمكين تصحيح أخطاء WebView</string>
|
<string name="enable_web_debugging">تمكين تصحيح أخطاء WebView</string>
|
||||||
<string name="enable_web_debugging_summary">يمكن استخدامه لتصحيح أخطاء WebUI، يرجى تمكينه فقط عند الحاجة.</string>
|
<string name="enable_web_debugging_summary">يمكن استخدامه لتصحيح أخطاء WebUI، يرجى تمكينه فقط عند الحاجة.</string>
|
||||||
<string name="install_next">التالي</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="select_file">اختيار ملف</string>
|
||||||
<string name="direct_install">تثبيت مباشر (موصى به)</string>
|
<string name="direct_install">تثبيت مباشر (موصى به)</string>
|
||||||
<string name="install_inactive_slot">التثبيت على فتحة غير نشطة (بعد OTA)</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.
|
\nN\'utilisez cette option qu\'une fois la mise à jour OTA terminée.
|
||||||
\nContinuer ?</string>
|
\nContinuer ?</string>
|
||||||
<string name="install_next">Suivant</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>
|
</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!
|
<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.
|
\nSó use esta opção após a conclusão do OTA.
|
||||||
\nDeseja continuar?</string>
|
\nDeseja continuar?</string>
|
||||||
<string name="downloading">Baixando %1$s</string>
|
|
||||||
<string name="install_next">Próximo</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>
|
</resources>
|
||||||
@@ -112,8 +112,6 @@
|
|||||||
<string name="open">Открыть</string>
|
<string name="open">Открыть</string>
|
||||||
<string name="enable_web_debugging">Включить отладку WebView</string>
|
<string name="enable_web_debugging">Включить отладку WebView</string>
|
||||||
<string name="enable_web_debugging_summary">Используется для отладки веб-интерфейса. Пожалуйста, включайте только при необходимости.</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="direct_install">Прямая установка (Рекомендуется)</string>
|
||||||
<string name="install_inactive_slot">Установка в неактивный слот (После OTA)</string>
|
<string name="install_inactive_slot">Установка в неактивный слот (После OTA)</string>
|
||||||
<string name="install_next">Далее</string>
|
<string name="install_next">Далее</string>
|
||||||
|
|||||||
@@ -117,6 +117,4 @@
|
|||||||
\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.
|
\nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın.
|
||||||
\nDevam edilsin mi?</string>
|
\nDevam edilsin mi?</string>
|
||||||
<string name="install_next">Sonraki</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>
|
</resources>
|
||||||
@@ -113,7 +113,5 @@
|
|||||||
<string name="install_inactive_slot">安装到未使用的槽位(OTA 后)</string>
|
<string name="install_inactive_slot">安装到未使用的槽位(OTA 后)</string>
|
||||||
<string name="install_inactive_slot_warning">将在重启后强制切换到另一个槽位!\n注意只能在 OTA 更新完成后的重启之前使用。\n确认?</string>
|
<string name="install_inactive_slot_warning">将在重启后强制切换到另一个槽位!\n注意只能在 OTA 更新完成后的重启之前使用。\n确认?</string>
|
||||||
<string name="install_next">下一步</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>
|
<string name="select_file_tip">建议选择 %1$s 分区镜像</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -115,6 +115,4 @@
|
|||||||
\n請問是否繼續?</string>
|
\n請問是否繼續?</string>
|
||||||
<string name="direct_install">直接安裝(建議)</string>
|
<string name="direct_install">直接安裝(建議)</string>
|
||||||
<string name="install_next">下一步</string>
|
<string name="install_next">下一步</string>
|
||||||
<string name="failed_to_fetch_lkm_url">取得 LKM 下載鏈接失敗:%1$s</string>
|
|
||||||
<string name="downloading">正在下載:%1$s</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -115,7 +115,5 @@
|
|||||||
<string name="install_inactive_slot">Install to Inactive Slot (After OTA)</string>
|
<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_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="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>
|
<string name="select_file_tip">%1$s partition image is recommended</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user