You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FoxMagiskModuleManager/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java

671 lines
32 KiB
Java

3 years ago
package com.fox2code.mmm.installer;
import android.content.Intent;
import android.content.pm.PackageManager;
2 years ago
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
3 years ago
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import com.fox2code.mmm.module.ActionButtonType;
import com.fox2code.mmm.AppUpdateManager;
2 years ago
import com.fox2code.mmm.BuildConfig;
3 years ago
import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R;
import com.fox2code.mmm.XHooks;
3 years ago
import com.fox2code.mmm.compat.CompatActivity;
2 years ago
import com.fox2code.mmm.utils.FastException;
3 years ago
import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Hashes;
3 years ago
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.PropUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
3 years ago
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.internal.UiThreadHandler;
3 years ago
import com.topjohnwu.superuser.io.SuFile;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
3 years ago
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
3 years ago
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
3 years ago
public class InstallerActivity extends CompatActivity {
private static final String TAG = "InstallerActivity";
public LinearProgressIndicator progressIndicator;
public ExtendedFloatingActionButton rebootFloatingButton;
3 years ago
public InstallerTerminal installerTerminal;
private File moduleCache;
private File toDelete;
private boolean textWrap;
private boolean canceled;
3 years ago
@Override
protected void onCreate(Bundle savedInstanceState) {
this.moduleCache = new File(this.getCacheDir(), "installer");
if (!this.moduleCache.exists() && !this.moduleCache.mkdirs())
Log.e(TAG, "Failed to mkdir module cache dir!");
super.onCreate(savedInstanceState);
this.setDisplayHomeAsUpEnabled(true);
setActionBarBackground(null);
this.setOnBackPressedCallback(a -> {
this.canceled = true;
return false;
});
3 years ago
final Intent intent = this.getIntent();
final String target;
final String name;
final String checksum;
2 years ago
final boolean noExtensions;
final boolean rootless;
3 years ago
// 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;
}
2 years ago
target = intent.getStringExtra(Constants.EXTRA_INSTALL_PATH);
name = intent.getStringExtra(Constants.EXTRA_INSTALL_NAME);
checksum = intent.getStringExtra(Constants.EXTRA_INSTALL_CHECKSUM);
2 years ago
noExtensions = intent.getBooleanExtra(// Allow intent to disable extensions
Constants.EXTRA_INSTALL_NO_EXTENSIONS, false);
rootless = intent.getBooleanExtra(// For debug only
Constants.EXTRA_INSTALL_TEST_ROOTLESS, false);
3 years ago
} else {
Toast.makeText(this, "Unknown intent!", Toast.LENGTH_SHORT).show();
this.forceBackPressed();
return;
}
2 years ago
Log.i(TAG, "Install link: " + target);
3 years ago
boolean urlMode = target.startsWith("http://") || target.startsWith("https://");
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setTitle(name);
this.textWrap = MainApplication.isTextWrapEnabled();
setContentView(this.textWrap ?
R.layout.installer_wrap : R.layout.installer);
int background;
int foreground;
if (MainApplication.getINSTANCE().isLightTheme() &&
!MainApplication.isForceDarkTerminal()) {
background = Color.WHITE;
foreground = Color.BLACK;
} else {
background = Color.BLACK;
foreground = Color.WHITE;
}
View horizontalScroller = findViewById(R.id.install_horizontal_scroller);
RecyclerView installTerminal;
3 years ago
this.progressIndicator = findViewById(R.id.progress_bar);
this.rebootFloatingButton = findViewById(R.id.install_terminal_reboot_fab);
this.installerTerminal = new InstallerTerminal(
installTerminal = findViewById(R.id.install_terminal), foreground);
(horizontalScroller != null ? horizontalScroller : installTerminal)
.setBackground(new ColorDrawable(background));
3 years ago
this.progressIndicator.setVisibility(View.GONE);
this.progressIndicator.setIndeterminate(true);
this.getWindow().setFlags( // Note: Doesn't require WAKELOCK permission
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
this.progressIndicator.setVisibility(View.VISIBLE);
if (urlMode) this.installerTerminal.addLine("- Downloading " + name);
new Thread(() -> {
File moduleCache = this.toDelete = urlMode ?
new File(this.moduleCache, "module.zip") : new File(target);
if (urlMode && moduleCache.exists() && !moduleCache.delete() &&
!new SuFile(moduleCache.getAbsolutePath()).delete())
Log.e(TAG, "Failed to delete module cache");
String errMessage = "Failed to download module zip";
try {
Log.i(TAG, (urlMode ? "Downloading: " : "Loading: ") + target);
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);
3 years ago
});
}) : Files.readSU(moduleCache);
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);
this.runOnUiThread(() -> {
this.installerTerminal.addLine("- Checking file integrity");
});
if (!Hashes.checkSumMatch(rawModule, checksum)) {
this.setInstallStateFinished(false,
"! File integrity check failed", "");
return;
}
}
if (this.canceled) return;
Files.fixJavaZipHax(rawModule);
boolean noPatch = false;
boolean isModule = false;
boolean isAnyKernel = false;
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();
if (entryName.equals("anykernel.sh")) {
noPatch = true;
isAnyKernel = true;
break;
} else if (entryName.equals("module.prop")) {
noPatch = true;
isModule = true;
break;
} else if (entryName.endsWith("/anykernel.sh")) {
isAnyKernel = true;
} else if (entryName.endsWith("/module.prop")) {
isModule = true;
}
}
}
if (!isModule && !isAnyKernel) {
this.setInstallStateFinished(false,
"! File is not a valid magisk module", "");
return;
}
if (noPatch) {
if (urlMode) {
errMessage = "Failed to save module zip";
2 years ago
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
outputStream.write(rawModule);
outputStream.flush();
}
3 years ago
}
} else {
errMessage = "Failed to patch module zip";
3 years ago
this.runOnUiThread(() -> {
this.installerTerminal.addLine("- Patching " + name);
3 years ago
});
Log.i(TAG, "Patching: " + moduleCache.getName());
try (OutputStream outputStream = new FileOutputStream(moduleCache)) {
Files.patchModuleSimple(rawModule, outputStream);
outputStream.flush();
}
}
//noinspection UnusedAssignment (Important to avoid OutOfMemoryError)
rawModule = null; // Because reference is kept when calling doInstall
if (this.canceled) return;
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, "");
}
}, "Module install Thread").start();
3 years ago
}
private void doInstall(File file, boolean noExtensions, boolean rootless) {
if (this.canceled) return;
UiThreadHandler.runAndWait(() -> {
this.setOnBackPressedCallback(DISABLE_BACK_BUTTON);
this.setDisplayHomeAsUpEnabled(false);
});
3 years ago
Log.i(TAG, "Installing: " + moduleCache.getName());
InstallerController installerController = new InstallerController(
2 years ago
this.progressIndicator, this.installerTerminal,
file.getAbsoluteFile(), noExtensions);
InstallerMonitor installerMonitor;
Shell.Job installJob;
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;
}
installerMonitor = new InstallerMonitor(installScript);
installJob = Shell.cmd("export MMM_EXT_SUPPORT=1",
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
"sh \"" + installScript.getAbsolutePath() + "\"" +
" /dev/null 1 \"" + file.getAbsolutePath() + "\"")
.to(installerController, installerMonitor);
} else {
String arch32 = "true"; // Do nothing by default
boolean needs32bit = false;
String moduleId = null;
boolean anyKernel = false;
boolean magiskModule = false;
boolean anyKernelSystemLess = false;
File anyKernelInstallScript = new File(this.moduleCache, "update-binary");
try (ZipFile zipFile = new ZipFile(file)) {
ZipEntry anyKernelSh = zipFile.getEntry("anykernel.sh");
if (anyKernelSh != null) { // Check if module is AnyKernel module
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(zipFile.getInputStream(anyKernelSh)));
String line;
// Check if AnyKernel module support system-less
while ((line = bufferedReader.readLine()) != null) {
String trimmedLine = line.trim();
if (trimmedLine.equals("do.modules=1"))
anyKernel = true;
if (trimmedLine.equals("do.systemless=1"))
anyKernelSystemLess = true;
}
bufferedReader.close();
if (anyKernelSystemLess && anyKernel) {
anyKernelSystemLess = false;
ZipEntry updateBinary = zipFile.getEntry(
"META-INF/com/google/android/update-binary");
if (updateBinary != null) {
bufferedReader = new BufferedReader(
new InputStreamReader(zipFile.getInputStream(updateBinary)));
PrintStream printStream = new PrintStream(
new FileOutputStream(anyKernelInstallScript));
while ((line = bufferedReader.readLine()) != null) {
String trimmedLine = line.trim();
if (trimmedLine.equals("mount_all;") ||
trimmedLine.equals("umount_all;"))
continue; // Do not mount anything
line = line.replace("/sbin/sh", "/system/bin/sh");
int prePatch = line.length();
line = line.replace("/data/adb/modules/ak3-helper",
"/data/adb/modules-update/ak3-helper");
if (prePatch != line.length()) anyKernelSystemLess = true;
printStream.println(line);
}
printStream.close();
bufferedReader.close();
if (!anyKernelSystemLess) anyKernelInstallScript.delete();
}
}
anyKernel = true;
}
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;
}
ZipEntry moduleProp = zipFile.getEntry("module.prop");
magiskModule = moduleProp != null;
moduleId = PropUtils.readModuleId(zipFile
.getInputStream(zipFile.getEntry("module.prop")));
} catch (IOException ignored) {
}
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;
}
if (magiskModule && moduleId == null && !anyKernel) {
// Modules without module Ids are module installed by 3rd party software
this.setInstallStateFinished(false,
"! Magisk modules require a moduleId", null);
return;
}
if (Build.SUPPORTED_32_BIT_ABIS.length == 0) {
if (needs32bit) {
this.setInstallStateFinished(false,
"! This module can't be installed on a 64bit only system",
null);
return;
}
} else if (needs32bit || (compatFlags & AppUpdateManager.FLAG_COMPAT_NO_EXT) == 0) {
// 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";
}
String installCommand;
File installExecutable;
2 years ago
if (anyKernel && moduleId == null) { // AnyKernel modules don't have a moduleId
if (!anyKernelSystemLess) {
this.setInstallStateFinished(false,
"! This AnyKernel module only support recovery install", null);
return;
}
installExecutable = anyKernelInstallScript;
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 0 \"" + file.getAbsolutePath() + "\"";
} else if (InstallerInitializer.peekMagiskVersion() >=
Constants.MAGISK_VER_CODE_INSTALL_COMMAND &&
((compatFlags & AppUpdateManager.FLAG_COMPAT_MAGISK_CMD) != 0 ||
noExtensions || MainApplication.isUsingMagiskCommand())) {
installCommand = "magisk --install-module \"" + file.getAbsolutePath() + "\"";
installExecutable = new File(InstallerInitializer.peekMagiskPath()
.equals("/sbin") ? "/sbin/magisk" : "/system/bin/magisk");
} else if (moduleId != null) {
installExecutable = this.extractInstallScript("module_installer_compat.sh");
if (installExecutable == null) {
this.setInstallStateFinished(false,
"! Failed to extract module install script", null);
return;
}
installCommand = "sh \"" + installExecutable.getAbsolutePath() + "\"" +
" /dev/null 0 \"" + file.getAbsolutePath() + "\"";
} else {
this.setInstallStateFinished(false,
"! Zip file is not a valid Magisk or a AnyKernel module!", null);
return;
}
installerMonitor = new InstallerMonitor(installExecutable);
if (moduleId != null) installerMonitor.setForCleanUp(moduleId);
2 years ago
if (noExtensions) {
installJob = Shell.cmd(arch32, // No Extensions
2 years ago
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
installCommand).to(installerController, installerMonitor);
2 years ago
} else {
installJob = Shell.cmd(arch32, "export MMM_EXT_SUPPORT=1",
2 years ago
"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"),
2 years ago
"cd \"" + this.moduleCache.getAbsolutePath() + "\"",
installCommand).to(installerController, installerMonitor);
}
}
boolean success = installJob.exec().isSuccess();
// Wait one UI cycle before disabling controller or processing results
UiThreadHandler.runAndWait(() -> {
}); // to avoid race conditions
3 years ago
installerController.disable();
String message = "- Install successful";
if (!success) {
// Workaround busybox-ndk install recognized as failed when successful
if (this.installerTerminal.getLastLine().trim().equals("Done!")) {
success = true;
} else {
message = installerMonitor.doCleanUp();
}
3 years ago
}
this.setInstallStateFinished(success, message,
installerController.getSupportLink());
}
public static class InstallerController extends CallbackList<String> {
private final LinearProgressIndicator progressIndicator;
private final InstallerTerminal terminal;
private final File moduleFile;
2 years ago
private final boolean noExtension;
3 years ago
private boolean enabled, useExt;
private String supportLink = "";
private InstallerController(LinearProgressIndicator progressIndicator,
InstallerTerminal terminal, File moduleFile,
2 years ago
boolean noExtension) {
3 years ago
this.progressIndicator = progressIndicator;
this.terminal = terminal;
this.moduleFile = moduleFile;
2 years ago
this.noExtension = noExtension;
3 years ago
this.enabled = true;
this.useExt = false;
}
@Override
public void onAddElement(String s) {
if (!this.enabled) return;
Log.d(TAG, "MSG: " + s);
2 years ago
if ("#!useExt".equals(s.trim()) && !this.noExtension) {
3 years ago
this.useExt = true;
return;
}
if (this.useExt && s.startsWith("#!")) {
this.processCommand(s);
} else {
this.terminal.addLine(s.replace(
this.moduleFile.getAbsolutePath(),
this.moduleFile.getName()));
3 years ago
}
}
private void processCommand(String rawCommand) {
final String arg;
final String command;
int i = rawCommand.indexOf(' ');
if (i != -1) {
2 years ago
arg = rawCommand.substring(i + 1).trim();
3 years ago
command = rawCommand.substring(2, i);
} else {
arg = "";
command = rawCommand.substring(2);
}
switch (command) {
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":
2 years ago
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);
}
} else if (!rawCommand.trim().equals("#!showLoading")) {
this.progressIndicator.setProgressCompat(0, true);
this.progressIndicator.setMax(100);
if (this.progressIndicator.getVisibility() == View.VISIBLE) {
this.progressIndicator.setVisibility(View.GONE);
}
this.progressIndicator.setIndeterminate(true);
}
3 years ago
this.progressIndicator.setVisibility(View.VISIBLE);
break;
2 years ago
case "setLoading":
try {
this.progressIndicator.setProgressCompat(
Short.parseShort(arg), true);
} catch (Exception ignored) {
}
2 years ago
break;
3 years ago
case "hideLoading":
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;
}
}
public void disable() {
this.enabled = false;
}
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";
private final String installScriptErr;
public String lastCommand = "";
public String forCleanUp;
3 years ago
public InstallerMonitor(File installScript) {
super(Runnable::run);
this.installScriptErr =
installScript.getAbsolutePath() +
": /data/adb/modules_update/";
3 years ago
}
@Override
public void onAddElement(String s) {
Log.d(TAG, "Monitor: " + s);
this.lastCommand = s;
}
public void setForCleanUp(String forCleanUp) {
this.forCleanUp = forCleanUp;
}
3 years ago
private String doCleanUp() {
String installScriptErr = this.installScriptErr;
3 years ago
// 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);
}
} 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");
3 years ago
}
return DEFAULT_ERR;
}
}
private File extractInstallScript(String script) {
File compatInstallScript = new File(this.moduleCache, script);
if (!compatInstallScript.exists() || compatInstallScript.length() == 0) {
3 years ago
try {
Files.write(compatInstallScript, Files.readAllBytes(
this.getAssets().open(script)));
3 years ago
} catch (IOException e) {
compatInstallScript.delete();
Log.e(TAG, "Failed to extract " + script, e);
3 years ago
return null;
}
}
return compatInstallScript;
}
@SuppressWarnings("SameParameterValue")
private void setInstallStateFinished(boolean success, String message, String optionalLink) {
3 years ago
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(() -> {
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 0);
3 years ago
this.setOnBackPressedCallback(null);
this.setDisplayHomeAsUpEnabled(true);
this.progressIndicator.setVisibility(View.GONE);
// This should be improved ?
String reboot_cmd = "/system/bin/svc power reboot || /system/bin/reboot";
rebootFloatingButton.setOnClickListener(_view -> {
if (MainApplication.shouldPreventReboot()) {
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();
}
});
3 years ago
if (message != null && !message.isEmpty())
this.installerTerminal.addLine(message);
if (!optionalLink.isEmpty()) {
this.setActionBarExtraMenuButton(ActionButtonType.supportIconForUrl(optionalLink),
menu -> {
IntentHelper.openUrl(this, optionalLink);
return true;
});
3 years ago
} else if (success) {
this.rebootFloatingButton.setVisibility(View.VISIBLE);
3 years ago
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 {
XHooks.checkConfigTargetExists(this, configPkg, config);
3 years ago
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");
}
}
}
});
}
}