almost done with kotlin migration

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/89/head
androidacy-user 2 years ago
parent 547a509e64
commit 8de65a47f6

@ -51,6 +51,7 @@ import com.fox2code.mmm.repo.RepoModule
import com.fox2code.mmm.settings.SettingsActivity import com.fox2code.mmm.settings.SettingsActivity
import com.fox2code.mmm.utils.ExternalHelper import com.fox2code.mmm.utils.ExternalHelper
import com.fox2code.mmm.utils.RuntimeUtils import com.fox2code.mmm.utils.RuntimeUtils
import com.fox2code.mmm.utils.SyncManager
import com.fox2code.mmm.utils.io.net.Http.Companion.cleanDnsCache import com.fox2code.mmm.utils.io.net.Http.Companion.cleanDnsCache
import com.fox2code.mmm.utils.io.net.Http.Companion.hasWebView import com.fox2code.mmm.utils.io.net.Http.Companion.hasWebView
import com.fox2code.mmm.utils.realm.ReposList import com.fox2code.mmm.utils.realm.ReposList
@ -388,17 +389,20 @@ class MainActivity : FoxActivity(), OnRefreshListener, SearchView.OnQueryTextLis
if (BuildConfig.DEBUG) Timber.i("Check Update") if (BuildConfig.DEBUG) Timber.i("Check Update")
// update repos // update repos
if (hasWebView()) { if (hasWebView()) {
RepoManager.getINSTANCE()!!.update { value: Double -> val updateListener: SyncManager.UpdateListener = object : SyncManager.UpdateListener {
runOnUiThread(if (max == 0) Runnable { override fun update(value: Double) {
progressIndicator.setProgressCompat( runOnUiThread(if (max == 0) Runnable {
(value * PRECISION).toInt(), true progressIndicator.setProgressCompat(
) (value * PRECISION).toInt(), true
} else Runnable { )
progressIndicator.setProgressCompat( } else Runnable {
(value * PRECISION * 0.75f).toInt(), true progressIndicator.setProgressCompat(
) (value * PRECISION * 0.75f).toInt(), true
}) )
})
}
} }
RepoManager.getINSTANCE()!!.update(updateListener)
} }
// various notifications // various notifications
NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder) NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder)
@ -606,13 +610,16 @@ class MainActivity : FoxActivity(), OnRefreshListener, SearchView.OnQueryTextLis
progressIndicator!!.max = PRECISION progressIndicator!!.max = PRECISION
} }
if (BuildConfig.DEBUG) Timber.i("Check Update") if (BuildConfig.DEBUG) Timber.i("Check Update")
RepoManager.getINSTANCE()!!.update { value: Double -> val updateListener: SyncManager.UpdateListener = object : SyncManager.UpdateListener {
runOnUiThread { override fun update(value: Double) {
progressIndicator!!.setProgressCompat( runOnUiThread {
(value * PRECISION).toInt(), true progressIndicator!!.setProgressCompat(
) (value * PRECISION).toInt(), true
)
}
} }
} }
RepoManager.getINSTANCE()!!.update(updateListener)
runOnUiThread { runOnUiThread {
progressIndicator!!.setProgressCompat(PRECISION, true) progressIndicator!!.setProgressCompat(PRECISION, true)
progressIndicator!!.visibility = View.GONE progressIndicator!!.visibility = View.GONE
@ -647,17 +654,20 @@ class MainActivity : FoxActivity(), OnRefreshListener, SearchView.OnQueryTextLis
Thread({ Thread({
cleanDnsCache() // Allow DNS reload from network cleanDnsCache() // Allow DNS reload from network
val max = instance!!.getUpdatableModuleCount() val max = instance!!.getUpdatableModuleCount()
RepoManager.getINSTANCE()!!.update { value: Double -> val updateListener: SyncManager.UpdateListener = object : SyncManager.UpdateListener {
runOnUiThread(if (max == 0) Runnable { override fun update(value: Double) {
progressIndicator!!.setProgressCompat( runOnUiThread(if (max == 0) Runnable {
(value * PRECISION).toInt(), true progressIndicator!!.setProgressCompat(
) (value * PRECISION).toInt(), true
} else Runnable { )
progressIndicator!!.setProgressCompat( } else Runnable {
(value * PRECISION * 0.75f).toInt(), true progressIndicator!!.setProgressCompat(
) (value * PRECISION * 0.75f).toInt(), true
}) )
})
}
} }
RepoManager.getINSTANCE()!!.update(updateListener)
NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder) NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder)
if (!NotificationType.NO_INTERNET.shouldRemove()) { if (!NotificationType.NO_INTERNET.shouldRemove()) {
moduleViewListBuilderOnline.addNotification(NotificationType.NO_INTERNET) moduleViewListBuilderOnline.addNotification(NotificationType.NO_INTERNET)

@ -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…
Cancel
Save