almost done with kotlin migration
Signed-off-by: androidacy-user <opensource@androidacy.com>pull/89/head
parent
547a509e64
commit
8de65a47f6
@ -1,29 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
|
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
|
||||||
*/
|
*/
|
||||||
|
package com.fox2code.mmm.utils
|
||||||
|
|
||||||
package com.fox2code.mmm.utils;
|
import android.app.AlarmManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.fox2code.mmm.MainActivity
|
||||||
|
import java.util.concurrent.ThreadLocalRandom
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
enum class ProcessHelper {
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.fox2code.mmm.MainActivity;
|
|
||||||
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
public enum ProcessHelper {
|
|
||||||
;
|
;
|
||||||
private static final int sPendingIntentId = ThreadLocalRandom.current().nextInt(100, 1000000 + 1);
|
|
||||||
|
|
||||||
public static void restartApplicationProcess(Context context) {
|
companion object {
|
||||||
Intent mStartActivity = new Intent(context, MainActivity.class);
|
private val sPendingIntentId = ThreadLocalRandom.current().nextInt(100, 1000000 + 1)
|
||||||
mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
@JvmStatic
|
||||||
PendingIntent mPendingIntent = PendingIntent.getActivity(context, sPendingIntentId,
|
fun restartApplicationProcess(context: Context) {
|
||||||
mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
val mStartActivity = Intent(context, MainActivity::class.java)
|
||||||
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
mStartActivity.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
|
val mPendingIntent = PendingIntent.getActivity(
|
||||||
System.exit(0); // Exit app process
|
context, sPendingIntentId,
|
||||||
|
mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
val mgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
|
mgr[AlarmManager.RTC, System.currentTimeMillis() + 100] = mPendingIntent
|
||||||
|
exitProcess(0) // Exit app process
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,88 +1,79 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
|
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
|
||||||
*/
|
*/
|
||||||
|
package com.fox2code.mmm.utils
|
||||||
package com.fox2code.mmm.utils;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager that want both to be thread safe and not to worry about thread safety
|
* Manager that want both to be thread safe and not to worry about thread safety
|
||||||
* {@link #scan()} and {@link #update(UpdateListener)} can be called from multiple
|
* [.scan] and [.update] can be called from multiple
|
||||||
* thread at the same time, {@link #scanInternal(UpdateListener)} will only be
|
* thread at the same time, [.scanInternal] will only be
|
||||||
* called from one thread at a time only.
|
* called from one thread at a time only.
|
||||||
*/
|
*/
|
||||||
public abstract class SyncManager {
|
abstract class SyncManager {
|
||||||
private static final UpdateListener NO_OP = value -> {
|
protected val syncLock = Any()
|
||||||
};
|
private var syncing = false
|
||||||
protected final Object syncLock = new Object();
|
private var lastSync: Long = 0
|
||||||
private boolean syncing;
|
fun scanAsync() {
|
||||||
private long lastSync;
|
if (!syncing) {
|
||||||
|
Thread({ this.scan() }, "Scan Thread").start()
|
||||||
public final void scanAsync() {
|
|
||||||
if (!this.syncing) {
|
|
||||||
new Thread(this::scan, "Scan Thread").start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void scan() {
|
fun scan() {
|
||||||
this.update(null);
|
update(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiThread friendly method
|
// MultiThread friendly method
|
||||||
public final void update(@Nullable UpdateListener updateListener) {
|
@Suppress("NAME_SHADOWING")
|
||||||
if (updateListener == null) updateListener = NO_OP;
|
fun update(updateListener: UpdateListener?) {
|
||||||
if (!this.syncing) {
|
var updateListener = updateListener
|
||||||
|
if (updateListener == null) updateListener = NO_OP
|
||||||
|
if (!syncing) {
|
||||||
// Do scan
|
// Do scan
|
||||||
synchronized (this.syncLock) {
|
synchronized(syncLock) {
|
||||||
if (System.currentTimeMillis() < this.lastSync + 50L)
|
if (System.currentTimeMillis() < lastSync + 50L) return // Skip sync if it was synced too recently
|
||||||
return; // Skip sync if it was synced too recently
|
syncing = true
|
||||||
this.syncing = true;
|
|
||||||
try {
|
try {
|
||||||
this.scanInternal(updateListener);
|
scanInternal(updateListener)
|
||||||
} finally {
|
} finally {
|
||||||
this.lastSync = System.currentTimeMillis();
|
lastSync = System.currentTimeMillis()
|
||||||
this.syncing = false;
|
syncing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Wait for current scan
|
// Wait for current scan
|
||||||
synchronized (this.syncLock) {
|
synchronized(syncLock) { Thread.yield() }
|
||||||
Thread.yield();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause execution until the scan is completed if one is currently running
|
// Pause execution until the scan is completed if one is currently running
|
||||||
public final void afterScan() {
|
fun afterScan() {
|
||||||
if (this.syncing) synchronized (this.syncLock) {
|
if (syncing) synchronized(syncLock) { Thread.yield() }
|
||||||
Thread.yield();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void runAfterScan(Runnable runnable) {
|
fun runAfterScan(runnable: Runnable) {
|
||||||
synchronized (this.syncLock) {
|
synchronized(syncLock) { runnable.run() }
|
||||||
runnable.run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void afterUpdate() {
|
fun afterUpdate() {
|
||||||
if (this.syncing) synchronized (this.syncLock) {
|
if (syncing) synchronized(syncLock) { Thread.yield() }
|
||||||
Thread.yield();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void runAfterUpdate(Runnable runnable) {
|
fun runAfterUpdate(runnable: Runnable) {
|
||||||
synchronized (this.syncLock) {
|
synchronized(syncLock) { runnable.run() }
|
||||||
runnable.run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method can't be called twice at the same time.
|
// This method can't be called twice at the same time.
|
||||||
protected abstract void scanInternal(@NonNull UpdateListener updateListener);
|
protected abstract fun scanInternal(updateListener: UpdateListener)
|
||||||
|
interface UpdateListener {
|
||||||
|
fun update(value: Double)
|
||||||
|
}
|
||||||
|
|
||||||
public interface UpdateListener {
|
companion object {
|
||||||
void update(double value);
|
private val NO_OP: UpdateListener = object : UpdateListener {
|
||||||
|
override fun update(value: Double) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,190 +1,199 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
|
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
|
||||||
*/
|
*/
|
||||||
|
package com.fox2code.mmm.utils
|
||||||
|
|
||||||
package com.fox2code.mmm.utils;
|
import android.content.DialogInterface
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.fox2code.foxcompat.app.FoxActivity
|
||||||
|
import com.fox2code.mmm.BuildConfig
|
||||||
|
import com.fox2code.mmm.R
|
||||||
|
import com.fox2code.mmm.installer.InstallerInitializer.Companion.peekMagiskPath
|
||||||
|
import com.fox2code.mmm.utils.BudgetProgressDialog.Companion.build
|
||||||
|
import com.fox2code.mmm.utils.IntentHelper.Companion.openInstaller
|
||||||
|
import com.fox2code.mmm.utils.io.Files.Companion.getFileName
|
||||||
|
import com.fox2code.mmm.utils.io.Files.Companion.getFileSize
|
||||||
|
import com.fox2code.mmm.utils.io.PropUtils.Companion.readModulePropSimple
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
import android.net.Uri;
|
class ZipFileOpener : FoxActivity() {
|
||||||
import android.os.Build;
|
var loading: AlertDialog? = null
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
|
|
||||||
import com.fox2code.foxcompat.app.FoxActivity;
|
|
||||||
import com.fox2code.mmm.BuildConfig;
|
|
||||||
import com.fox2code.mmm.R;
|
|
||||||
import com.fox2code.mmm.installer.InstallerInitializer;
|
|
||||||
import com.fox2code.mmm.utils.io.Files;
|
|
||||||
import com.fox2code.mmm.utils.io.PropUtils;
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class ZipFileOpener extends FoxActivity {
|
|
||||||
AlertDialog loading = null;
|
|
||||||
|
|
||||||
// Adds us as a handler for zip files, so we can pass them to the installer
|
// Adds us as a handler for zip files, so we can pass them to the installer
|
||||||
// We should have a content uri provided to us.
|
// We should have a content uri provided to us.
|
||||||
@Override
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
super.onCreate(savedInstanceState)
|
||||||
super.onCreate(savedInstanceState);
|
loading = build(this, R.string.loading, R.string.zip_unpacking)
|
||||||
loading = BudgetProgressDialog.build(this, R.string.loading, R.string.zip_unpacking);
|
Thread(Runnable {
|
||||||
new Thread(() -> {
|
Timber.d("onCreate: %s", intent)
|
||||||
Timber.d("onCreate: %s", getIntent());
|
val zipFile: File
|
||||||
File zipFile;
|
val uri = intent.data
|
||||||
Uri uri = getIntent().getData();
|
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
Timber.e("onCreate: No data provided");
|
Timber.e("onCreate: No data provided")
|
||||||
runOnUiThread(() -> {
|
runOnUiThread {
|
||||||
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show()
|
||||||
finishAndRemoveTask();
|
finishAndRemoveTask()
|
||||||
});
|
}
|
||||||
return;
|
return@Runnable
|
||||||
}
|
}
|
||||||
// Try to copy the file to our cache
|
// Try to copy the file to our cache
|
||||||
try {
|
try {
|
||||||
// check if its a file over 10MB
|
// check if its a file over 10MB
|
||||||
Long fileSize = Files.getFileSize(this, uri);
|
var fileSize = getFileSize(this, uri)
|
||||||
if (fileSize == null) fileSize = 0L;
|
if (fileSize == null) fileSize = 0L
|
||||||
if (1000L * 1000 * 10 < fileSize) {
|
if (1000L * 1000 * 10 < fileSize) {
|
||||||
runOnUiThread(() -> loading.show());
|
runOnUiThread { loading!!.show() }
|
||||||
}
|
}
|
||||||
zipFile = File.createTempFile("module", ".zip", getCacheDir());
|
zipFile = File.createTempFile("module", ".zip", cacheDir)
|
||||||
try (InputStream inputStream = getContentResolver().openInputStream(uri); FileOutputStream outputStream = new FileOutputStream(zipFile)) {
|
contentResolver.openInputStream(uri).use { inputStream ->
|
||||||
if (inputStream == null) {
|
FileOutputStream(zipFile).use { outputStream ->
|
||||||
Timber.e("onCreate: Failed to open input stream");
|
if (inputStream == null) {
|
||||||
runOnUiThread(() -> {
|
Timber.e("onCreate: Failed to open input stream")
|
||||||
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show();
|
runOnUiThread {
|
||||||
finishAndRemoveTask();
|
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG)
|
||||||
});
|
.show()
|
||||||
return;
|
finishAndRemoveTask()
|
||||||
}
|
}
|
||||||
byte[] buffer = new byte[4096];
|
return@Runnable
|
||||||
int read;
|
}
|
||||||
while ((read = inputStream.read(buffer)) != -1) {
|
val buffer = ByteArray(4096)
|
||||||
outputStream.write(buffer, 0, read);
|
var read: Int
|
||||||
|
while (inputStream.read(buffer).also { read = it } != -1) {
|
||||||
|
outputStream.write(buffer, 0, read)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (
|
} catch (e: Exception) {
|
||||||
Exception e) {
|
Timber.e(e, "onCreate: Failed to copy zip file")
|
||||||
Timber.e(e, "onCreate: Failed to copy zip file");
|
runOnUiThread {
|
||||||
runOnUiThread(() -> {
|
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show()
|
||||||
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show();
|
finishAndRemoveTask()
|
||||||
finishAndRemoveTask();
|
}
|
||||||
});
|
return@Runnable
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Ensure zip is not empty
|
// Ensure zip is not empty
|
||||||
if (zipFile.length() == 0) {
|
if (zipFile.length() == 0L) {
|
||||||
Timber.e("onCreate: Zip file is empty");
|
Timber.e("onCreate: Zip file is empty")
|
||||||
runOnUiThread(() -> {
|
runOnUiThread {
|
||||||
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show()
|
||||||
finishAndRemoveTask();
|
finishAndRemoveTask()
|
||||||
});
|
}
|
||||||
return;
|
return@Runnable
|
||||||
} else {
|
} else {
|
||||||
Timber.d("onCreate: Zip file is " + zipFile.length() + " bytes");
|
Timber.d("onCreate: Zip file is " + zipFile.length() + " bytes")
|
||||||
}
|
}
|
||||||
ZipEntry entry;
|
var entry: ZipEntry?
|
||||||
ZipFile zip = null;
|
var zip: ZipFile? = null
|
||||||
// Unpack the zip to validate it's a valid magisk module
|
// Unpack the zip to validate it's a valid magisk module
|
||||||
// It needs to have, at the bare minimum, a module.prop file. Everything else is technically optional.
|
// It needs to have, at the bare minimum, a module.prop file. Everything else is technically optional.
|
||||||
// First, check if it's a zip file
|
// First, check if it's a zip file
|
||||||
try {
|
try {
|
||||||
zip = new ZipFile(zipFile);
|
zip = ZipFile(zipFile)
|
||||||
if ((entry = zip.getEntry("module.prop")) == null) {
|
if (zip.getEntry("module.prop").also { entry = it } == null) {
|
||||||
Timber.e("onCreate: Zip file is not a valid magisk module");
|
Timber.e("onCreate: Zip file is not a valid magisk module")
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
Timber.d("onCreate: Zip file contents: %s", zip.stream().map(ZipEntry::getName).reduce((a, b) -> a + ", " + b).orElse("empty"));
|
Timber.d(
|
||||||
|
"onCreate: Zip file contents: %s",
|
||||||
|
zip.stream().map { obj: ZipEntry -> obj.name }
|
||||||
|
.reduce { a: String, b: String -> "$a, $b" }.orElse("empty")
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Timber.d("onCreate: Zip file contents cannot be listed on this version of android");
|
Timber.d("onCreate: Zip file contents cannot be listed on this version of android")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runOnUiThread(() -> {
|
runOnUiThread {
|
||||||
Toast.makeText(this, R.string.invalid_format, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.invalid_format, Toast.LENGTH_LONG).show()
|
||||||
finishAndRemoveTask();
|
finishAndRemoveTask()
|
||||||
});
|
}
|
||||||
return;
|
return@Runnable
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "onCreate: Failed to open zip file")
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show()
|
||||||
|
finishAndRemoveTask()
|
||||||
}
|
}
|
||||||
} catch (
|
|
||||||
Exception e) {
|
|
||||||
Timber.e(e, "onCreate: Failed to open zip file");
|
|
||||||
runOnUiThread(() -> {
|
|
||||||
Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show();
|
|
||||||
finishAndRemoveTask();
|
|
||||||
});
|
|
||||||
if (zip != null) {
|
if (zip != null) {
|
||||||
try {
|
try {
|
||||||
zip.close();
|
zip.close()
|
||||||
} catch (IOException exception) {
|
} catch (exception: IOException) {
|
||||||
Timber.e(Log.getStackTraceString(exception));
|
Timber.e(Log.getStackTraceString(exception))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return@Runnable
|
||||||
}
|
}
|
||||||
Timber.d("onCreate: Zip file is valid");
|
Timber.d("onCreate: Zip file is valid")
|
||||||
String moduleInfo;
|
var moduleInfo: String?
|
||||||
try {
|
try {
|
||||||
moduleInfo = PropUtils.readModulePropSimple(zip.getInputStream(entry), "name");
|
moduleInfo = readModulePropSimple(zip.getInputStream(entry), "name")
|
||||||
if (moduleInfo == null) {
|
if (moduleInfo == null) {
|
||||||
moduleInfo = PropUtils.readModulePropSimple(zip.getInputStream(entry), "id");
|
moduleInfo = readModulePropSimple(zip.getInputStream(entry), "id")
|
||||||
}
|
}
|
||||||
if (moduleInfo == null) {
|
if (moduleInfo == null) {
|
||||||
throw new NullPointerException("moduleInfo is null, check earlier logs for root cause");
|
throw NullPointerException("moduleInfo is null, check earlier logs for root cause")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "onCreate: Failed to load module id")
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(this, R.string.zip_prop_load_failed, Toast.LENGTH_LONG).show()
|
||||||
|
finishAndRemoveTask()
|
||||||
}
|
}
|
||||||
} catch (
|
|
||||||
Exception e) {
|
|
||||||
Timber.e(e, "onCreate: Failed to load module id");
|
|
||||||
runOnUiThread(() -> {
|
|
||||||
Toast.makeText(this, R.string.zip_prop_load_failed, Toast.LENGTH_LONG).show();
|
|
||||||
finishAndRemoveTask();
|
|
||||||
});
|
|
||||||
try {
|
try {
|
||||||
zip.close();
|
zip.close()
|
||||||
} catch (IOException exception) {
|
} catch (exception: IOException) {
|
||||||
Timber.e(Log.getStackTraceString(exception));
|
Timber.e(Log.getStackTraceString(exception))
|
||||||
}
|
}
|
||||||
return;
|
return@Runnable
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
zip.close();
|
zip.close()
|
||||||
} catch (IOException exception) {
|
} catch (exception: IOException) {
|
||||||
Timber.e(Log.getStackTraceString(exception));
|
Timber.e(Log.getStackTraceString(exception))
|
||||||
|
}
|
||||||
|
val finalModuleInfo: String = moduleInfo
|
||||||
|
runOnUiThread {
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(getString(R.string.zip_security_warning, finalModuleInfo))
|
||||||
|
.setMessage(
|
||||||
|
getString(
|
||||||
|
R.string.zip_intent_module_install,
|
||||||
|
finalModuleInfo,
|
||||||
|
getFileName(this, uri)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setNegativeButton(R.string.no) { d: DialogInterface, _: Int ->
|
||||||
|
d.dismiss()
|
||||||
|
finishAndRemoveTask()
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.yes) { d: DialogInterface, _: Int ->
|
||||||
|
d.dismiss()
|
||||||
|
// Pass the file to the installer
|
||||||
|
val compatActivity = getFoxActivity(this)
|
||||||
|
openInstaller(
|
||||||
|
compatActivity, zipFile.absolutePath,
|
||||||
|
compatActivity.getString(
|
||||||
|
R.string.local_install_title
|
||||||
|
), null, null, false,
|
||||||
|
BuildConfig.DEBUG && // Use debug mode if no root
|
||||||
|
peekMagiskPath() == null
|
||||||
|
)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
loading!!.dismiss()
|
||||||
}
|
}
|
||||||
String finalModuleInfo = moduleInfo;
|
}).start()
|
||||||
runOnUiThread(() -> {
|
|
||||||
new MaterialAlertDialogBuilder(this)
|
|
||||||
.setTitle(getString(R.string.zip_security_warning, finalModuleInfo))
|
|
||||||
.setMessage(getString(R.string.zip_intent_module_install, finalModuleInfo, Files.getFileName(this, uri)))
|
|
||||||
.setCancelable(false)
|
|
||||||
.setNegativeButton(R.string.no, (d, i) -> {
|
|
||||||
d.dismiss();
|
|
||||||
finishAndRemoveTask();
|
|
||||||
})
|
|
||||||
.setPositiveButton(R.string.yes, (d, i) -> {
|
|
||||||
d.dismiss();
|
|
||||||
// Pass the file to the installer
|
|
||||||
FoxActivity compatActivity = FoxActivity.getFoxActivity(this);
|
|
||||||
IntentHelper.openInstaller(compatActivity, zipFile.getAbsolutePath(),
|
|
||||||
compatActivity.getString(
|
|
||||||
R.string.local_install_title), null, null, false,
|
|
||||||
BuildConfig.DEBUG && // Use debug mode if no root
|
|
||||||
InstallerInitializer.peekMagiskPath() == null);
|
|
||||||
finish();
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
loading.dismiss();
|
|
||||||
});
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue