2021-10-01 20:19:42 +00:00
|
|
|
package com.fox2code.mmm.installer;
|
|
|
|
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.pm.PackageManager;
|
2022-01-23 18:17:08 +00:00
|
|
|
import android.content.res.Resources;
|
2021-10-12 20:06:17 +00:00
|
|
|
import android.graphics.Color;
|
|
|
|
import android.graphics.drawable.ColorDrawable;
|
2022-02-11 14:25:41 +00:00
|
|
|
import android.os.Build;
|
2021-10-01 20:19:42 +00:00
|
|
|
import android.os.Bundle;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.view.KeyEvent;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.WindowManager;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
2022-01-26 19:34:36 +00:00
|
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
|
2022-06-01 15:50:26 +00:00
|
|
|
import com.fox2code.androidansi.AnsiConstants;
|
|
|
|
import com.fox2code.androidansi.AnsiParser;
|
2022-03-02 21:20:41 +00:00
|
|
|
import com.fox2code.mmm.AppUpdateManager;
|
2022-01-23 18:17:08 +00:00
|
|
|
import com.fox2code.mmm.BuildConfig;
|
2021-10-01 20:19:42 +00:00
|
|
|
import com.fox2code.mmm.Constants;
|
|
|
|
import com.fox2code.mmm.MainApplication;
|
|
|
|
import com.fox2code.mmm.R;
|
2022-03-26 19:39:47 +00:00
|
|
|
import com.fox2code.mmm.XHooks;
|
2021-10-01 20:19:42 +00:00
|
|
|
import com.fox2code.mmm.compat.CompatActivity;
|
2022-06-01 15:50:26 +00:00
|
|
|
import com.fox2code.mmm.module.ActionButtonType;
|
2022-01-23 18:17:08 +00:00
|
|
|
import com.fox2code.mmm.utils.FastException;
|
2021-10-01 20:19:42 +00:00
|
|
|
import com.fox2code.mmm.utils.Files;
|
2022-01-29 17:06:28 +00:00
|
|
|
import com.fox2code.mmm.utils.Hashes;
|
2021-10-01 20:19:42 +00:00
|
|
|
import com.fox2code.mmm.utils.Http;
|
|
|
|
import com.fox2code.mmm.utils.IntentHelper;
|
2022-03-02 21:20:41 +00:00
|
|
|
import com.fox2code.mmm.utils.PropUtils;
|
2022-04-26 16:12:40 +00:00
|
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
2022-04-25 16:52:48 +00:00
|
|
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
|
2021-10-01 20:19:42 +00:00
|
|
|
import com.google.android.material.progressindicator.LinearProgressIndicator;
|
|
|
|
import com.topjohnwu.superuser.CallbackList;
|
|
|
|
import com.topjohnwu.superuser.Shell;
|
2021-10-04 12:47:13 +00:00
|
|
|
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
2021-10-01 20:19:42 +00:00
|
|
|
import com.topjohnwu.superuser.io.SuFile;
|
|
|
|
|
2022-05-03 10:57:01 +00:00
|
|
|
import java.io.BufferedReader;
|
2022-02-05 15:11:32 +00:00
|
|
|
import java.io.ByteArrayInputStream;
|
2021-10-01 20:19:42 +00:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.IOException;
|
2022-05-03 10:57:01 +00:00
|
|
|
import java.io.InputStreamReader;
|
2021-10-01 20:19:42 +00:00
|
|
|
import java.io.OutputStream;
|
2022-02-05 15:11:32 +00:00
|
|
|
import java.util.zip.ZipEntry;
|
2022-02-11 14:25:41 +00:00
|
|
|
import java.util.zip.ZipFile;
|
2022-02-05 15:11:32 +00:00
|
|
|
import java.util.zip.ZipInputStream;
|
2021-10-01 20:19:42 +00:00
|
|
|
|
|
|
|
public class InstallerActivity extends CompatActivity {
|
|
|
|
private static final String TAG = "InstallerActivity";
|
|
|
|
public LinearProgressIndicator progressIndicator;
|
2022-04-25 16:52:48 +00:00
|
|
|
public ExtendedFloatingActionButton rebootFloatingButton;
|
2021-10-01 20:19:42 +00:00
|
|
|
public InstallerTerminal installerTerminal;
|
|
|
|
private File moduleCache;
|
|
|
|
private File toDelete;
|
2022-01-26 19:34:36 +00:00
|
|
|
private boolean textWrap;
|
2022-01-31 22:12:33 +00:00
|
|
|
private boolean canceled;
|
2022-05-07 11:40:37 +00:00
|
|
|
private boolean warnReboot;
|
2021-10-01 20:19:42 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
2022-05-07 11:40:37 +00:00
|
|
|
this.warnReboot = false;
|
2021-10-01 20:19:42 +00:00
|
|
|
this.moduleCache = new File(this.getCacheDir(), "installer");
|
|
|
|
if (!this.moduleCache.exists() && !this.moduleCache.mkdirs())
|
|
|
|
Log.e(TAG, "Failed to mkdir module cache dir!");
|
2021-10-12 20:06:17 +00:00
|
|
|
super.onCreate(savedInstanceState);
|
2022-01-31 22:12:33 +00:00
|
|
|
this.setDisplayHomeAsUpEnabled(true);
|
2022-02-23 20:30:51 +00:00
|
|
|
setActionBarBackground(null);
|
2022-01-31 22:12:33 +00:00
|
|
|
this.setOnBackPressedCallback(a -> {
|
2022-04-26 16:12:40 +00:00
|
|
|
this.canceled = true;
|
|
|
|
return false;
|
2022-01-31 22:12:33 +00:00
|
|
|
});
|
2021-10-01 20:19:42 +00:00
|
|
|
final Intent intent = this.getIntent();
|
|
|
|
final String target;
|
|
|
|
final String name;
|
2022-01-29 17:06:28 +00:00
|
|
|
final String checksum;
|
2022-01-23 18:17:08 +00:00
|
|
|
final boolean noExtensions;
|
2022-01-26 19:34:36 +00:00
|
|
|
final boolean rootless;
|
2021-10-01 20:19:42 +00:00
|
|
|
// Should we allow 3rd part app to install modules?
|
|
|
|
if (Constants.INTENT_INSTALL_INTERNAL.equals(intent.getAction())) {
|
|
|
|
if (!MainApplication.checkSecret(intent)) {
|
|
|
|
Log.e(TAG, "Security check failed!");
|
|
|
|
this.forceBackPressed();
|
|
|
|
return;
|
|
|
|
}
|
2022-01-23 18:17:08 +00:00
|
|
|
target = intent.getStringExtra(Constants.EXTRA_INSTALL_PATH);
|
|
|
|
name = intent.getStringExtra(Constants.EXTRA_INSTALL_NAME);
|
2022-01-29 17:06:28 +00:00
|
|
|
checksum = intent.getStringExtra(Constants.EXTRA_INSTALL_CHECKSUM);
|
2022-01-23 18:17:08 +00:00
|
|
|
noExtensions = intent.getBooleanExtra(// Allow intent to disable extensions
|
|
|
|
Constants.EXTRA_INSTALL_NO_EXTENSIONS, false);
|
2022-01-26 19:34:36 +00:00
|
|
|
rootless = intent.getBooleanExtra(// For debug only
|
|
|
|
Constants.EXTRA_INSTALL_TEST_ROOTLESS, false);
|
2021-10-01 20:19:42 +00:00
|
|
|
} else {
|
|
|
|
Toast.makeText(this, "Unknown intent!", Toast.LENGTH_SHORT).show();
|
|
|
|
this.forceBackPressed();
|
|
|
|
return;
|
|
|
|
}
|
2022-01-23 18:17:08 +00:00
|
|
|
Log.i(TAG, "Install link: " + target);
|
2021-10-01 20:19:42 +00:00
|
|
|
boolean urlMode = target.startsWith("http://") || target.startsWith("https://");
|
|
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
|
|
setTitle(name);
|
2022-02-11 14:25:41 +00:00
|
|
|
this.textWrap = MainApplication.isTextWrapEnabled();
|
|
|
|
setContentView(this.textWrap ?
|
2022-04-26 16:12:40 +00:00
|
|
|
R.layout.installer_wrap : R.layout.installer);
|
2021-10-12 20:06:17 +00:00
|
|
|
int background;
|
|
|
|
int foreground;
|
|
|
|
if (MainApplication.getINSTANCE().isLightTheme() &&
|
|
|
|
!MainApplication.isForceDarkTerminal()) {
|
|
|
|
background = Color.WHITE;
|
|
|
|
foreground = Color.BLACK;
|
|
|
|
} else {
|
|
|
|
background = Color.BLACK;
|
|
|
|
foreground = Color.WHITE;
|
|
|
|
}
|
2022-01-26 19:34:36 +00:00
|
|
|
View horizontalScroller = findViewById(R.id.install_horizontal_scroller);
|
|
|
|
RecyclerView installTerminal;
|
2021-10-01 20:19:42 +00:00
|
|
|
this.progressIndicator = findViewById(R.id.progress_bar);
|
2022-04-25 16:52:48 +00:00
|
|
|
this.rebootFloatingButton = findViewById(R.id.install_terminal_reboot_fab);
|
2021-10-12 20:06:17 +00:00
|
|
|
this.installerTerminal = new InstallerTerminal(
|
2022-06-01 15:50:26 +00:00
|
|
|
installTerminal = findViewById(R.id.install_terminal),
|
|
|
|
this.isLightTheme(), foreground);
|
2022-01-26 19:34:36 +00:00
|
|
|
(horizontalScroller != null ? horizontalScroller : installTerminal)
|
|
|
|
.setBackground(new ColorDrawable(background));
|
2021-10-01 20:19:42 +00:00
|
|
|
this.progressIndicator.setVisibility(View.GONE);
|
|
|
|
this.progressIndicator.setIndeterminate(true);
|
2022-02-24 19:27:10 +00:00
|
|
|
this.getWindow().setFlags( // Note: Doesn't require WAKELOCK permission
|
|
|
|
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
|
|
|
|
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
2022-05-03 10:57:01 +00:00
|
|
|
this.progressIndicator.setVisibility(View.VISIBLE);
|
2022-05-03 14:18:58 +00:00
|
|
|
if (urlMode) this.installerTerminal.addLine("- Downloading " + name);
|
2022-05-03 10:57:01 +00:00
|
|
|
new Thread(() -> {
|
|
|
|
File moduleCache = this.toDelete = urlMode ?
|
2022-05-03 14:18:58 +00:00
|
|
|
new File(this.moduleCache, "module.zip") : new File(target);
|
|
|
|
if (urlMode && moduleCache.exists() && !moduleCache.delete() &&
|
2022-05-03 10:57:01 +00:00
|
|
|
!new SuFile(moduleCache.getAbsolutePath()).delete())
|
|
|
|
Log.e(TAG, "Failed to delete module cache");
|
|
|
|
String errMessage = "Failed to download module zip";
|
|
|
|
try {
|
2022-05-03 14:18:58 +00:00
|
|
|
Log.i(TAG, (urlMode ? "Downloading: " : "Loading: ") + target);
|
2022-05-03 10:57:01 +00:00
|
|
|
byte[] rawModule = urlMode ? Http.doHttpGet(target, (progress, max, done) -> {
|
|
|
|
if (max <= 0 && this.progressIndicator.isIndeterminate())
|
|
|
|
return;
|
|
|
|
this.runOnUiThread(() -> {
|
|
|
|
this.progressIndicator.setIndeterminate(false);
|
|
|
|
this.progressIndicator.setMax(max);
|
|
|
|
this.progressIndicator.setProgressCompat(progress, true);
|
2021-10-01 20:19:42 +00:00
|
|
|
});
|
2022-05-03 14:18:58 +00:00
|
|
|
}) : Files.readSU(moduleCache);
|
2022-05-03 10:57:01 +00:00
|
|
|
this.runOnUiThread(() -> {
|
|
|
|
this.progressIndicator.setVisibility(View.GONE);
|
|
|
|
this.progressIndicator.setIndeterminate(true);
|
|
|
|
});
|
|
|
|
if (this.canceled) return;
|
|
|
|
if (checksum != null && !checksum.isEmpty()) {
|
|
|
|
Log.d(TAG, "Checking for checksum: " + checksum);
|
2022-02-22 00:45:56 +00:00
|
|
|
this.runOnUiThread(() -> {
|
2022-05-03 10:57:01 +00:00
|
|
|
this.installerTerminal.addLine("- Checking file integrity");
|
2022-02-22 00:45:56 +00:00
|
|
|
});
|
2022-05-03 10:57:01 +00:00
|
|
|
if (!Hashes.checkSumMatch(rawModule, checksum)) {
|
|
|
|
this.setInstallStateFinished(false,
|
|
|
|
"! File integrity check failed", "");
|
|
|
|
return;
|
2022-01-29 17:06:28 +00:00
|
|
|
}
|
2022-05-03 10:57:01 +00:00
|
|
|
}
|
|
|
|
if (this.canceled) return;
|
|
|
|
Files.fixJavaZipHax(rawModule);
|
|
|
|
boolean noPatch = false;
|
|
|
|
boolean isModule = false;
|
2022-05-14 21:47:43 +00:00
|
|
|
boolean isAnyKernel3 = false;
|
2022-05-03 10:57:01 +00:00
|
|
|
errMessage = "File is not a valid zip file";
|
|
|
|
try (ZipInputStream zipInputStream = new ZipInputStream(
|
|
|
|
new ByteArrayInputStream(rawModule))) {
|
|
|
|
ZipEntry zipEntry;
|
|
|
|
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
|
|
|
String entryName = zipEntry.getName();
|
2022-05-14 21:47:43 +00:00
|
|
|
if (entryName.equals("tools/ak3-core.sh")) {
|
2022-05-03 10:57:01 +00:00
|
|
|
noPatch = true;
|
2022-05-14 21:47:43 +00:00
|
|
|
isAnyKernel3 = true;
|
2022-05-03 10:57:01 +00:00
|
|
|
break;
|
|
|
|
} else if (entryName.equals("module.prop")) {
|
|
|
|
noPatch = true;
|
|
|
|
isModule = true;
|
|
|
|
break;
|
2022-05-14 21:47:43 +00:00
|
|
|
} else if (entryName.endsWith("/tools/ak3-core.sh")) {
|
|
|
|
isAnyKernel3 = true;
|
2022-05-03 10:57:01 +00:00
|
|
|
} else if (entryName.endsWith("/module.prop")) {
|
|
|
|
isModule = true;
|
2022-02-05 15:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-03 10:57:01 +00:00
|
|
|
}
|
2022-05-14 21:47:43 +00:00
|
|
|
if (!isModule && !isAnyKernel3) {
|
2022-05-03 10:57:01 +00:00
|
|
|
this.setInstallStateFinished(false,
|
2022-05-15 23:04:08 +00:00
|
|
|
"! File is not a valid Magisk module or AnyKernel3 zip", "");
|
2022-05-03 10:57:01 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (noPatch) {
|
|
|
|
if (urlMode) {
|
2022-02-05 15:11:32 +00:00
|
|
|
errMessage = "Failed to save module zip";
|
2022-01-23 18:17:08 +00:00
|
|
|
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
|
|
|
|
outputStream.write(rawModule);
|
|
|
|
outputStream.flush();
|
|
|
|
}
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
2022-05-03 10:57:01 +00:00
|
|
|
} else {
|
|
|
|
errMessage = "Failed to patch module zip";
|
2021-10-01 20:19:42 +00:00
|
|
|
this.runOnUiThread(() -> {
|
2022-05-03 10:57:01 +00:00
|
|
|
this.installerTerminal.addLine("- Patching " + name);
|
2021-10-01 20:19:42 +00:00
|
|
|
});
|
2022-05-03 10:57:01 +00:00
|
|
|
Log.i(TAG, "Patching: " + moduleCache.getName());
|
|
|
|
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
|
|
|
|
Files.patchModuleSimple(rawModule, outputStream);
|
|
|
|
outputStream.flush();
|
2022-01-29 17:06:28 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-03 10:57:01 +00:00
|
|
|
//noinspection UnusedAssignment (Important to avoid OutOfMemoryError)
|
|
|
|
rawModule = null; // Because reference is kept when calling doInstall
|
2022-01-31 22:12:33 +00:00
|
|
|
if (this.canceled) return;
|
2022-05-03 10:57:01 +00:00
|
|
|
this.runOnUiThread(() -> {
|
|
|
|
this.installerTerminal.addLine("- Installing " + name);
|
|
|
|
});
|
|
|
|
errMessage = "Failed to install module zip";
|
|
|
|
this.doInstall(moduleCache, noExtensions, rootless);
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.e(TAG, errMessage, e);
|
|
|
|
this.setInstallStateFinished(false,
|
|
|
|
"! " + errMessage, "");
|
2022-01-29 17:06:28 +00:00
|
|
|
}
|
2022-05-03 10:57:01 +00:00
|
|
|
}, "Module install Thread").start();
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
|
2022-01-29 17:06:28 +00:00
|
|
|
|
2022-04-26 16:12:40 +00:00
|
|
|
private void doInstall(File file, boolean noExtensions, boolean rootless) {
|
2022-01-31 22:12:33 +00:00
|
|
|
if (this.canceled) return;
|
2022-02-02 14:53:28 +00:00
|
|
|
UiThreadHandler.runAndWait(() -> {
|
|
|
|
this.setOnBackPressedCallback(DISABLE_BACK_BUTTON);
|
|
|
|
this.setDisplayHomeAsUpEnabled(false);
|
|
|
|
});
|
2021-10-01 20:19:42 +00:00
|
|
|
Log.i(TAG, "Installing: " + moduleCache.getName());
|
|
|
|
InstallerController installerController = new InstallerController(
|
2022-01-23 18:17:08 +00:00
|
|
|
this.progressIndicator, this.installerTerminal,
|
|
|
|
file.getAbsoluteFile(), noExtensions);
|
2021-10-23 16:14:40 +00:00
|
|
|
InstallerMonitor installerMonitor;
|
|
|
|
Shell.Job installJob;
|
2022-01-26 19:34:36 +00:00
|
|
|
if (rootless) { // rootless is only used for debugging
|
|
|
|
File installScript = this.extractInstallScript("module_installer_test.sh");
|
|
|
|
if (installScript == null) {
|
|
|
|
this.setInstallStateFinished(false,
|
|
|
|
"! Failed to extract test install script", "");
|
|
|
|
return;
|
|
|
|
}
|
2022-06-01 15:50:26 +00:00
|
|
|
this.installerTerminal.enableAnsi();
|
|
|
|
// Extract customize.sh manually in rootless mode because unzip might not exists
|
|
|
|
try (ZipFile zipFile = new ZipFile(file)) {
|
|
|
|
ZipEntry zipEntry = zipFile.getEntry("customize.sh");
|
|
|
|
if (zipEntry != null) {
|
|
|
|
try (FileOutputStream fileOutputStream = new FileOutputStream(
|
|
|
|
new File(file.getParentFile(), "customize.sh"))) {
|
|
|
|
Files.copy(zipFile.getInputStream(zipEntry), fileOutputStream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.d(TAG, "Failed ot extract install script via java code", e);
|
|
|
|
}
|
2022-01-26 19:34:36 +00:00
|
|
|
installerMonitor = new InstallerMonitor(installScript);
|
2022-03-10 20:16:53 +00:00
|
|
|
installJob = Shell.cmd("export MMM_EXT_SUPPORT=1",
|
2022-06-01 15:50:26 +00:00
|
|
|
"export MMM_USER_LANGUAGE=" + (MainApplication.isForceEnglish() ? "en-US" :
|
|
|
|
Resources.getSystem().getConfiguration().locale.toLanguageTag()),
|
|
|
|
"export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME,
|
|
|
|
"export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"),
|
|
|
|
AnsiConstants.ANSI_CMD_SUPPORT,
|
2022-01-26 19:34:36 +00:00
|
|
|
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
|
|
|
|
"sh \"" + installScript.getAbsolutePath() + "\"" +
|
2022-05-14 21:47:43 +00:00
|
|
|
" 3 0 \"" + file.getAbsolutePath() + "\"")
|
2022-01-26 19:34:36 +00:00
|
|
|
.to(installerController, installerMonitor);
|
2022-01-31 15:05:03 +00:00
|
|
|
} else {
|
2022-02-11 14:25:41 +00:00
|
|
|
String arch32 = "true"; // Do nothing by default
|
2022-03-02 21:20:41 +00:00
|
|
|
boolean needs32bit = false;
|
|
|
|
String moduleId = null;
|
2022-05-14 21:47:43 +00:00
|
|
|
boolean anyKernel3 = false;
|
2022-05-03 10:57:01 +00:00
|
|
|
boolean magiskModule = false;
|
2022-05-14 21:47:43 +00:00
|
|
|
String MAGISK_PATH = InstallerInitializer.peekMagiskPath();
|
|
|
|
if (MAGISK_PATH == null) {
|
|
|
|
this.setInstallStateFinished(false, "! Unable to resolve magisk path", "");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
String ASH = MAGISK_PATH + "/.magisk/busybox/busybox ash";
|
2022-03-02 21:20:41 +00:00
|
|
|
try (ZipFile zipFile = new ZipFile(file)) {
|
2022-05-07 11:40:37 +00:00
|
|
|
// Check if module is AnyKernel module
|
2022-05-14 21:47:43 +00:00
|
|
|
if (zipFile.getEntry("tools/ak3-core.sh") != null) {
|
2022-05-07 11:40:37 +00:00
|
|
|
ZipEntry updateBinary = zipFile.getEntry(
|
|
|
|
"META-INF/com/google/android/update-binary");
|
|
|
|
if (updateBinary != null) {
|
|
|
|
BufferedReader bufferedReader = new BufferedReader(
|
|
|
|
new InputStreamReader(zipFile.getInputStream(updateBinary)));
|
|
|
|
String line;
|
|
|
|
while ((line = bufferedReader.readLine()) != null) {
|
2022-05-14 21:47:43 +00:00
|
|
|
if (line.contains("AnyKernel3")) {
|
|
|
|
anyKernel3 = true;
|
|
|
|
break;
|
|
|
|
}
|
2022-05-03 10:57:01 +00:00
|
|
|
}
|
2022-05-07 11:40:37 +00:00
|
|
|
bufferedReader.close();
|
2022-05-03 10:57:01 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-02 21:20:41 +00:00
|
|
|
if (zipFile.getEntry( // Check if module hard require 32bit support
|
|
|
|
"common/addon/Volume-Key-Selector/tools/arm64/keycheck") == null &&
|
|
|
|
zipFile.getEntry(
|
|
|
|
"common/addon/Volume-Key-Selector/install.sh") != null) {
|
|
|
|
needs32bit = true;
|
|
|
|
}
|
2022-05-03 10:57:01 +00:00
|
|
|
ZipEntry moduleProp = zipFile.getEntry("module.prop");
|
|
|
|
magiskModule = moduleProp != null;
|
2022-03-02 21:20:41 +00:00
|
|
|
moduleId = PropUtils.readModuleId(zipFile
|
|
|
|
.getInputStream(zipFile.getEntry("module.prop")));
|
2022-04-26 16:12:40 +00:00
|
|
|
} catch (IOException ignored) {
|
|
|
|
}
|
2022-03-02 21:20:41 +00:00
|
|
|
int compatFlags = AppUpdateManager.getFlagsForModule(moduleId);
|
|
|
|
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NEED_32BIT) != 0)
|
|
|
|
needs32bit = true;
|
|
|
|
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) != 0)
|
|
|
|
noExtensions = true;
|
|
|
|
if (moduleId != null && (moduleId.isEmpty() ||
|
|
|
|
moduleId.contains("/") || moduleId.contains("\0") ||
|
|
|
|
(moduleId.startsWith(".") && moduleId.endsWith(".")))) {
|
|
|
|
this.setInstallStateFinished(false,
|
|
|
|
"! This module contain a dangerous moduleId",
|
|
|
|
null);
|
|
|
|
return;
|
|
|
|
}
|
2022-05-14 21:47:43 +00:00
|
|
|
if (magiskModule && moduleId == null && !anyKernel3) {
|
2022-05-03 10:57:01 +00:00
|
|
|
// Modules without module Ids are module installed by 3rd party software
|
|
|
|
this.setInstallStateFinished(false,
|
|
|
|
"! Magisk modules require a moduleId", null);
|
|
|
|
return;
|
|
|
|
}
|
2022-05-14 21:47:43 +00:00
|
|
|
if (anyKernel3) {
|
|
|
|
installerController.useRecoveryExt();
|
|
|
|
} else if (Build.SUPPORTED_32_BIT_ABIS.length == 0) {
|
2022-02-11 14:25:41 +00:00
|
|
|
if (needs32bit) {
|
|
|
|
this.setInstallStateFinished(false,
|
|
|
|
"! This module can't be installed on a 64bit only system",
|
|
|
|
null);
|
|
|
|
return;
|
|
|
|
}
|
2022-03-02 21:20:41 +00:00
|
|
|
} else if (needs32bit || (compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) == 0) {
|
2022-02-11 14:25:41 +00:00
|
|
|
// Restore Magisk legacy stuff for retro compatibility
|
|
|
|
if (Build.SUPPORTED_32_BIT_ABIS[0].contains("arm"))
|
|
|
|
arch32 = "export ARCH32=arm";
|
|
|
|
if (Build.SUPPORTED_32_BIT_ABIS[0].contains("x86"))
|
|
|
|
arch32 = "export ARCH32=x86";
|
|
|
|
}
|
2022-01-31 15:05:03 +00:00
|
|
|
String installCommand;
|
|
|
|
File installExecutable;
|
2022-06-07 19:25:12 +00:00
|
|
|
if (anyKernel3 && moduleId == null) { // AnyKernel zip don't have a moduleId
|
2022-05-07 11:40:37 +00:00
|
|
|
this.warnReboot = true; // We should probably re-flash magisk...
|
2022-06-07 19:25:12 +00:00
|
|
|
installExecutable = this.extractInstallScript("anykernel3_installer.sh");
|
2022-05-14 21:47:43 +00:00
|
|
|
if (installExecutable == null) {
|
|
|
|
this.setInstallStateFinished(false,
|
2022-06-17 14:07:09 +00:00
|
|
|
"! Failed to extract AnyKernel3 install script", "");
|
2022-05-14 21:47:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-05-07 11:40:37 +00:00
|
|
|
// "unshare -m" is needed to force mount namespace isolation.
|
|
|
|
// This allow AnyKernel to mess-up with mounts point without crashing the system!
|
2022-05-14 21:47:43 +00:00
|
|
|
installCommand = "unshare -m " + ASH + " \"" +
|
|
|
|
installExecutable.getAbsolutePath() + "\"" +
|
|
|
|
" 3 1 \"" + file.getAbsolutePath() + "\"";
|
2022-05-03 10:57:01 +00:00
|
|
|
} else if (InstallerInitializer.peekMagiskVersion() >=
|
2022-01-31 15:05:03 +00:00
|
|
|
Constants.MAGISK_VER_CODE_INSTALL_COMMAND &&
|
2022-03-02 21:20:41 +00:00
|
|
|
((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 ||
|
|
|
|
noExtensions || MainApplication.isUsingMagiskCommand())) {
|
2022-01-31 15:05:03 +00:00
|
|
|
installCommand = "magisk --install-module \"" + file.getAbsolutePath() + "\"";
|
2022-05-14 21:47:43 +00:00
|
|
|
installExecutable = new File(MAGISK_PATH.equals("/sbin") ?
|
|
|
|
"/sbin/magisk" : "/system/bin/magisk");
|
2022-05-03 10:57:01 +00:00
|
|
|
} else if (moduleId != null) {
|
2022-01-31 15:05:03 +00:00
|
|
|
installExecutable = this.extractInstallScript("module_installer_compat.sh");
|
|
|
|
if (installExecutable == null) {
|
|
|
|
this.setInstallStateFinished(false,
|
2022-06-17 14:07:09 +00:00
|
|
|
"! Failed to extract Magisk module install script", "");
|
2022-01-31 15:05:03 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-05-14 21:47:43 +00:00
|
|
|
installCommand = ASH + " \"" +
|
|
|
|
installExecutable.getAbsolutePath() + "\"" +
|
|
|
|
" 3 1 \"" + file.getAbsolutePath() + "\"";
|
2022-05-03 10:57:01 +00:00
|
|
|
} else {
|
|
|
|
this.setInstallStateFinished(false,
|
2022-06-17 14:07:09 +00:00
|
|
|
"! Zip file is not a valid Magisk module or AnyKernel3 zip!", "");
|
2022-05-03 10:57:01 +00:00
|
|
|
return;
|
2022-01-31 15:05:03 +00:00
|
|
|
}
|
|
|
|
installerMonitor = new InstallerMonitor(installExecutable);
|
2022-03-02 21:20:41 +00:00
|
|
|
if (moduleId != null) installerMonitor.setForCleanUp(moduleId);
|
2022-05-14 21:47:43 +00:00
|
|
|
if (noExtensions) {
|
2022-06-01 15:50:26 +00:00
|
|
|
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_FORCE_ANSI) != 0)
|
|
|
|
this.installerTerminal.enableAnsi();
|
|
|
|
else this.installerTerminal.disableAnsi();
|
2022-05-14 21:47:43 +00:00
|
|
|
installJob = Shell.cmd(arch32, "export BOOTMODE=true", // No Extensions
|
2022-06-01 15:50:26 +00:00
|
|
|
this.installerTerminal.isAnsiEnabled() ?
|
|
|
|
AnsiConstants.ANSI_CMD_SUPPORT : "true",
|
2022-01-23 18:17:08 +00:00
|
|
|
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
|
2022-01-31 15:05:03 +00:00
|
|
|
installCommand).to(installerController, installerMonitor);
|
2022-01-23 18:17:08 +00:00
|
|
|
} else {
|
2022-06-01 15:50:26 +00:00
|
|
|
if ((compatFlags & AppUpdateManager.FLAG_COMPAT_NO_ANSI) != 0)
|
2022-07-02 12:58:30 +00:00
|
|
|
this.installerTerminal.disableAnsi();
|
|
|
|
else this.installerTerminal.enableAnsi();
|
2022-03-10 20:16:53 +00:00
|
|
|
installJob = Shell.cmd(arch32, "export MMM_EXT_SUPPORT=1",
|
2022-05-07 11:40:37 +00:00
|
|
|
"export MMM_USER_LANGUAGE=" + (MainApplication.isForceEnglish() ? "en-US" :
|
|
|
|
Resources.getSystem().getConfiguration().locale.toLanguageTag()),
|
2022-01-23 18:17:08 +00:00
|
|
|
"export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME,
|
2022-01-26 19:34:36 +00:00
|
|
|
"export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"),
|
2022-06-01 15:50:26 +00:00
|
|
|
this.installerTerminal.isAnsiEnabled() ?
|
|
|
|
AnsiConstants.ANSI_CMD_SUPPORT : "true",
|
2022-05-14 21:47:43 +00:00
|
|
|
"export BOOTMODE=true", anyKernel3 ? "export AK3TMPFS=" +
|
|
|
|
InstallerInitializer.peekMagiskPath() + "/ak3tmpfs" :
|
|
|
|
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
|
2022-01-31 15:05:03 +00:00
|
|
|
installCommand).to(installerController, installerMonitor);
|
2021-10-23 16:14:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
boolean success = installJob.exec().isSuccess();
|
2021-10-04 12:47:13 +00:00
|
|
|
// Wait one UI cycle before disabling controller or processing results
|
2022-06-01 15:50:26 +00:00
|
|
|
UiThreadHandler.runAndWait(() -> {}); // to avoid race conditions
|
2021-10-01 20:19:42 +00:00
|
|
|
installerController.disable();
|
|
|
|
String message = "- Install successful";
|
|
|
|
if (!success) {
|
2021-10-04 12:47:13 +00:00
|
|
|
// Workaround busybox-ndk install recognized as failed when successful
|
|
|
|
if (this.installerTerminal.getLastLine().trim().equals("Done!")) {
|
|
|
|
success = true;
|
|
|
|
} else {
|
|
|
|
message = installerMonitor.doCleanUp();
|
|
|
|
}
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
this.setInstallStateFinished(success, message,
|
|
|
|
installerController.getSupportLink());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class InstallerController extends CallbackList<String> {
|
|
|
|
private final LinearProgressIndicator progressIndicator;
|
|
|
|
private final InstallerTerminal terminal;
|
2021-10-04 12:47:13 +00:00
|
|
|
private final File moduleFile;
|
2022-01-23 18:17:08 +00:00
|
|
|
private final boolean noExtension;
|
2022-05-14 21:47:43 +00:00
|
|
|
private boolean enabled, useExt,
|
|
|
|
useRecovery, isRecoveryBar;
|
2021-10-01 20:19:42 +00:00
|
|
|
private String supportLink = "";
|
|
|
|
|
2021-10-04 12:47:13 +00:00
|
|
|
private InstallerController(LinearProgressIndicator progressIndicator,
|
2022-04-26 16:12:40 +00:00
|
|
|
InstallerTerminal terminal, File moduleFile,
|
2022-01-23 18:17:08 +00:00
|
|
|
boolean noExtension) {
|
2021-10-01 20:19:42 +00:00
|
|
|
this.progressIndicator = progressIndicator;
|
|
|
|
this.terminal = terminal;
|
2021-10-04 12:47:13 +00:00
|
|
|
this.moduleFile = moduleFile;
|
2022-01-23 18:17:08 +00:00
|
|
|
this.noExtension = noExtension;
|
2021-10-01 20:19:42 +00:00
|
|
|
this.enabled = true;
|
|
|
|
this.useExt = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAddElement(String s) {
|
|
|
|
if (!this.enabled) return;
|
|
|
|
Log.d(TAG, "MSG: " + s);
|
2022-01-23 18:17:08 +00:00
|
|
|
if ("#!useExt".equals(s.trim()) && !this.noExtension) {
|
2021-10-01 20:19:42 +00:00
|
|
|
this.useExt = true;
|
|
|
|
return;
|
|
|
|
}
|
2022-06-01 15:50:26 +00:00
|
|
|
s = AnsiParser.patchEscapeSequence(s);
|
2021-10-01 20:19:42 +00:00
|
|
|
if (this.useExt && s.startsWith("#!")) {
|
2022-05-14 21:47:43 +00:00
|
|
|
this.processCommand(s.substring(2));
|
|
|
|
} else if (this.useRecovery && s.startsWith("progress ")) {
|
|
|
|
String[] tokens = s.split(" ");
|
|
|
|
try {
|
|
|
|
float progress = Float.parseFloat(tokens[1]);
|
|
|
|
float max = Float.parseFloat(tokens[2]);
|
|
|
|
int progressInt;
|
|
|
|
if (max <= 0F) {
|
|
|
|
return;
|
|
|
|
} else if (progress >= max) {
|
|
|
|
progressInt = 256;
|
|
|
|
} else {
|
|
|
|
if (progress <= 0F) progress = 0F;
|
|
|
|
progressInt = (int) ((256D * progress) / max);
|
|
|
|
}
|
|
|
|
this.processCommand("showLoading 256");
|
|
|
|
this.processCommand("setLoading " + progressInt);
|
|
|
|
this.isRecoveryBar = true;
|
|
|
|
} catch (Exception ignored) {}
|
2021-10-01 20:19:42 +00:00
|
|
|
} else {
|
2021-10-04 12:47:13 +00:00
|
|
|
this.terminal.addLine(s.replace(
|
|
|
|
this.moduleFile.getAbsolutePath(),
|
|
|
|
this.moduleFile.getName()));
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void processCommand(String rawCommand) {
|
|
|
|
final String arg;
|
|
|
|
final String command;
|
|
|
|
int i = rawCommand.indexOf(' ');
|
2022-06-01 15:50:26 +00:00
|
|
|
if (i != -1 && rawCommand.length() != i + 1) {
|
2022-01-23 18:17:08 +00:00
|
|
|
arg = rawCommand.substring(i + 1).trim();
|
2022-05-14 21:47:43 +00:00
|
|
|
command = rawCommand.substring(0, i);
|
2021-10-01 20:19:42 +00:00
|
|
|
} else {
|
|
|
|
arg = "";
|
2022-05-14 21:47:43 +00:00
|
|
|
command = rawCommand;
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
switch (command) {
|
2022-05-14 21:47:43 +00:00
|
|
|
case "useRecovery":
|
|
|
|
this.useRecovery = true;
|
|
|
|
break;
|
2021-10-01 20:19:42 +00:00
|
|
|
case "addLine":
|
|
|
|
this.terminal.addLine(arg);
|
|
|
|
break;
|
|
|
|
case "setLastLine":
|
|
|
|
this.terminal.setLastLine(arg);
|
|
|
|
break;
|
|
|
|
case "clearTerminal":
|
|
|
|
this.terminal.clearTerminal();
|
|
|
|
break;
|
|
|
|
case "scrollUp":
|
|
|
|
this.terminal.scrollUp();
|
|
|
|
break;
|
|
|
|
case "scrollDown":
|
|
|
|
this.terminal.scrollDown();
|
|
|
|
break;
|
|
|
|
case "showLoading":
|
2022-05-14 21:47:43 +00:00
|
|
|
this.isRecoveryBar = false;
|
2022-01-23 18:17:08 +00:00
|
|
|
if (!arg.isEmpty()) {
|
|
|
|
try {
|
|
|
|
short s = Short.parseShort(arg);
|
|
|
|
if (s <= 0) throw FastException.INSTANCE;
|
|
|
|
this.progressIndicator.setMax(s);
|
|
|
|
this.progressIndicator.setIndeterminate(false);
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
this.progressIndicator.setProgressCompat(0, true);
|
|
|
|
this.progressIndicator.setMax(100);
|
|
|
|
if (this.progressIndicator.getVisibility() == View.VISIBLE) {
|
|
|
|
this.progressIndicator.setVisibility(View.GONE);
|
|
|
|
}
|
|
|
|
this.progressIndicator.setIndeterminate(true);
|
|
|
|
}
|
2022-05-14 21:47:43 +00:00
|
|
|
} else {
|
2022-01-23 18:17:08 +00:00
|
|
|
this.progressIndicator.setProgressCompat(0, true);
|
|
|
|
this.progressIndicator.setMax(100);
|
|
|
|
if (this.progressIndicator.getVisibility() == View.VISIBLE) {
|
|
|
|
this.progressIndicator.setVisibility(View.GONE);
|
|
|
|
}
|
|
|
|
this.progressIndicator.setIndeterminate(true);
|
|
|
|
}
|
2021-10-01 20:19:42 +00:00
|
|
|
this.progressIndicator.setVisibility(View.VISIBLE);
|
|
|
|
break;
|
2022-01-23 18:17:08 +00:00
|
|
|
case "setLoading":
|
2022-05-14 21:47:43 +00:00
|
|
|
this.isRecoveryBar = false;
|
2022-01-23 18:17:08 +00:00
|
|
|
try {
|
|
|
|
this.progressIndicator.setProgressCompat(
|
|
|
|
Short.parseShort(arg), true);
|
2022-04-26 16:12:40 +00:00
|
|
|
} catch (Exception ignored) {
|
|
|
|
}
|
2022-01-23 18:17:08 +00:00
|
|
|
break;
|
2021-10-01 20:19:42 +00:00
|
|
|
case "hideLoading":
|
2022-05-14 21:47:43 +00:00
|
|
|
this.isRecoveryBar = false;
|
2021-10-01 20:19:42 +00:00
|
|
|
this.progressIndicator.setVisibility(View.GONE);
|
|
|
|
break;
|
|
|
|
case "setSupportLink":
|
|
|
|
// Only set link if valid
|
|
|
|
if (arg.isEmpty() || (arg.startsWith("https://") &&
|
|
|
|
arg.indexOf('/', 8) > 8))
|
|
|
|
this.supportLink = arg;
|
|
|
|
break;
|
2022-06-01 15:50:26 +00:00
|
|
|
case "disableANSI":
|
|
|
|
this.terminal.disableAnsi();
|
|
|
|
break;
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-14 21:47:43 +00:00
|
|
|
public void useRecoveryExt() {
|
|
|
|
this.useRecovery = true;
|
|
|
|
}
|
|
|
|
|
2021-10-01 20:19:42 +00:00
|
|
|
public void disable() {
|
|
|
|
this.enabled = false;
|
2022-05-14 21:47:43 +00:00
|
|
|
if (this.isRecoveryBar) {
|
|
|
|
UiThreadHandler.runAndWait(() ->
|
|
|
|
this.processCommand("setLoading 256"));
|
|
|
|
}
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public String getSupportLink() {
|
|
|
|
return supportLink;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
|
|
int keyCode = event.getKeyCode();
|
|
|
|
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
|
|
|
keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) return true;
|
|
|
|
return super.dispatchKeyEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class InstallerMonitor extends CallbackList<String> {
|
|
|
|
private static final String DEFAULT_ERR = "! Install failed";
|
2022-02-11 14:25:41 +00:00
|
|
|
private final String installScriptErr;
|
2021-10-23 16:14:40 +00:00
|
|
|
public String lastCommand = "";
|
2022-03-02 21:20:41 +00:00
|
|
|
public String forCleanUp;
|
2021-10-01 20:19:42 +00:00
|
|
|
|
|
|
|
public InstallerMonitor(File installScript) {
|
|
|
|
super(Runnable::run);
|
2022-02-11 14:25:41 +00:00
|
|
|
this.installScriptErr =
|
|
|
|
installScript.getAbsolutePath() +
|
|
|
|
": /data/adb/modules_update/";
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAddElement(String s) {
|
|
|
|
Log.d(TAG, "Monitor: " + s);
|
|
|
|
this.lastCommand = s;
|
|
|
|
}
|
|
|
|
|
2022-03-02 21:20:41 +00:00
|
|
|
public void setForCleanUp(String forCleanUp) {
|
|
|
|
this.forCleanUp = forCleanUp;
|
|
|
|
}
|
|
|
|
|
2021-10-01 20:19:42 +00:00
|
|
|
private String doCleanUp() {
|
2022-02-11 14:25:41 +00:00
|
|
|
String installScriptErr = this.installScriptErr;
|
2021-10-01 20:19:42 +00:00
|
|
|
// This block is mainly to help fixing customize.sh syntax errors
|
|
|
|
if (this.lastCommand.startsWith(installScriptErr)) {
|
|
|
|
installScriptErr = this.lastCommand.substring(installScriptErr.length());
|
|
|
|
int i = installScriptErr.indexOf('/');
|
|
|
|
if (i == -1) return DEFAULT_ERR;
|
|
|
|
String module = installScriptErr.substring(0, i);
|
|
|
|
SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + module);
|
|
|
|
if (moduleUpdate.exists()) {
|
|
|
|
if (!moduleUpdate.deleteRecursive())
|
|
|
|
Log.e(TAG, "Failed to delete failed update");
|
|
|
|
return "Error: " + installScriptErr.substring(i + 1);
|
|
|
|
}
|
2022-03-02 21:20:41 +00:00
|
|
|
} else if (this.forCleanUp != null) {
|
|
|
|
SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + this.forCleanUp);
|
|
|
|
if (moduleUpdate.exists() && !moduleUpdate.deleteRecursive())
|
|
|
|
Log.e(TAG, "Failed to delete failed update");
|
2021-10-01 20:19:42 +00:00
|
|
|
}
|
|
|
|
return DEFAULT_ERR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 19:34:36 +00:00
|
|
|
private File extractInstallScript(String script) {
|
|
|
|
File compatInstallScript = new File(this.moduleCache, script);
|
2022-01-31 22:12:33 +00:00
|
|
|
if (!compatInstallScript.exists() || compatInstallScript.length() == 0) {
|
2021-10-01 20:19:42 +00:00
|
|
|
try {
|
|
|
|
Files.write(compatInstallScript, Files.readAllBytes(
|
2022-01-26 19:34:36 +00:00
|
|
|
this.getAssets().open(script)));
|
2021-10-01 20:19:42 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
compatInstallScript.delete();
|
2022-01-26 19:34:36 +00:00
|
|
|
Log.e(TAG, "Failed to extract " + script, e);
|
2021-10-01 20:19:42 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return compatInstallScript;
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("SameParameterValue")
|
2022-04-26 16:12:40 +00:00
|
|
|
private void setInstallStateFinished(boolean success, String message, String optionalLink) {
|
2022-06-01 15:50:26 +00:00
|
|
|
this.installerTerminal.disableAnsi();
|
2021-10-01 20:19:42 +00:00
|
|
|
if (success && toDelete != null && !toDelete.delete()) {
|
|
|
|
SuFile suFile = new SuFile(toDelete.getAbsolutePath());
|
|
|
|
if (suFile.exists() && !suFile.delete())
|
|
|
|
Log.w(TAG, "Failed to delete zip file");
|
|
|
|
else toDelete = null;
|
|
|
|
} else toDelete = null;
|
|
|
|
this.runOnUiThread(() -> {
|
2022-02-24 19:27:10 +00:00
|
|
|
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 0);
|
2021-10-01 20:19:42 +00:00
|
|
|
this.setOnBackPressedCallback(null);
|
|
|
|
this.setDisplayHomeAsUpEnabled(true);
|
|
|
|
this.progressIndicator.setVisibility(View.GONE);
|
2022-04-25 16:52:48 +00:00
|
|
|
|
|
|
|
// This should be improved ?
|
2022-04-26 16:12:40 +00:00
|
|
|
String reboot_cmd = "/system/bin/svc power reboot || /system/bin/reboot";
|
2022-05-07 11:40:37 +00:00
|
|
|
this.rebootFloatingButton.setOnClickListener(_view -> {
|
|
|
|
if (this.warnReboot || MainApplication.shouldPreventReboot()) {
|
2022-04-26 16:12:40 +00:00
|
|
|
MaterialAlertDialogBuilder builder =
|
|
|
|
new MaterialAlertDialogBuilder(this);
|
|
|
|
|
|
|
|
builder
|
|
|
|
.setTitle(R.string.install_terminal_reboot_now)
|
|
|
|
.setCancelable(false)
|
|
|
|
.setIcon(R.drawable.ic_reboot_24)
|
|
|
|
.setPositiveButton(R.string.yes, (x, y) -> {
|
|
|
|
Shell.cmd(reboot_cmd).submit();
|
|
|
|
})
|
|
|
|
.setNegativeButton(R.string.no, (x, y) -> {
|
|
|
|
x.dismiss();
|
|
|
|
}).show();
|
|
|
|
} else {
|
|
|
|
Shell.cmd(reboot_cmd).submit();
|
|
|
|
}
|
|
|
|
});
|
2022-05-07 11:40:37 +00:00
|
|
|
this.rebootFloatingButton.setVisibility(View.VISIBLE);
|
2022-04-25 16:52:48 +00:00
|
|
|
|
2021-10-01 20:19:42 +00:00
|
|
|
if (message != null && !message.isEmpty())
|
|
|
|
this.installerTerminal.addLine(message);
|
2022-06-17 14:07:09 +00:00
|
|
|
if (optionalLink != null && !optionalLink.isEmpty()) {
|
2021-10-01 20:19:42 +00:00
|
|
|
this.setActionBarExtraMenuButton(ActionButtonType.supportIconForUrl(optionalLink),
|
|
|
|
menu -> {
|
2022-04-26 16:12:40 +00:00
|
|
|
IntentHelper.openUrl(this, optionalLink);
|
|
|
|
return true;
|
|
|
|
});
|
2021-10-01 20:19:42 +00:00
|
|
|
} else if (success) {
|
|
|
|
final Intent intent = this.getIntent();
|
|
|
|
final String config = MainApplication.checkSecret(intent) ?
|
|
|
|
intent.getStringExtra(Constants.EXTRA_INSTALL_CONFIG) : null;
|
|
|
|
if (config != null && !config.isEmpty()) {
|
|
|
|
String configPkg = IntentHelper.getPackageOfConfig(config);
|
|
|
|
try {
|
2022-03-26 19:39:47 +00:00
|
|
|
XHooks.checkConfigTargetExists(this, configPkg, config);
|
2021-10-01 20:19:42 +00:00
|
|
|
this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24, menu -> {
|
|
|
|
IntentHelper.openConfig(this, config);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
|
|
Log.w(TAG, "Config package \"" +
|
|
|
|
configPkg + "\" missing for installer view");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|