refactor: bump deps + misc fixes

- Update various dependencies to newer versions.
- Update F-Droid builds as no longer maintained, and add note about it in README.
- Update minimum sdk version to 26.
- Remove unused `kapt` plugin.
- Refactor to replace `MainApplication.INSTANCE!!` with `MainApplication.getInstance()`
- Update NDK version to 28.0.13004108.
-Update comments and other minor tweaks.
- Add `ExpiredActivity` to handle expired builds.
- Make `isWrapped` a static final variable with appropriate assertion.
- Improve error handling for cases where activities are null.

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/164/head
androidacy-user 2 years ago
parent 11a0dc5d8b
commit 6b24e3a2f2

@ -19,6 +19,10 @@ We're actively working on its replacement though! We target a Q3 release for v3,
More news will be available soon! More news will be available soon!
**All builds before 2.3.7 are expired and cannot be used.**
##### F-Droid builds are no longer being maintained. Please do not use them.
[![Latest Release](https://img.shields.io/github/v/release/Androidacy/AndroidacyModuleManager?color=neon&label=Latest%20release&style=for-the-badge)](https://www.androidacy.com/downloads/?view=FoxMMM&utm_source=fox-readme&utm_medium=web&utm_campaign=github-readme) [![Latest Release](https://img.shields.io/github/v/release/Androidacy/AndroidacyModuleManager?color=neon&label=Latest%20release&style=for-the-badge)](https://www.androidacy.com/downloads/?view=FoxMMM&utm_source=fox-readme&utm_medium=web&utm_campaign=github-readme)
## Overview and Purpose ## Overview and Purpose

@ -12,7 +12,6 @@ plugins {
id("com.android.application") id("com.android.application")
id("com.mikepenz.aboutlibraries.plugin") id("com.mikepenz.aboutlibraries.plugin")
kotlin("android") kotlin("android")
kotlin("kapt")
id("com.google.devtools.ksp") version "2.0.21-1.0.25" id("com.google.devtools.ksp") version "2.0.21-1.0.25"
} }
android { android {
@ -29,8 +28,7 @@ android {
val timestamp = System.currentTimeMillis() val timestamp = System.currentTimeMillis()
namespace = "com.fox2code.mmm" namespace = "com.fox2code.mmm"
compileSdk = 34 compileSdk = 35
ndkVersion = "25.2.9519653"
signingConfigs { signingConfigs {
create("release") { create("release") {
if (File("signing.properties").exists()) { if (File("signing.properties").exists()) {
@ -330,7 +328,7 @@ androidComponents {
if (baseAbiCode != null) { if (baseAbiCode != null) {
// Assigns the new version code to output.versionCode, which changes the version code // Assigns the new version code to output.versionCode, which changes the version code
// for only the output APK, not for the variant itself. // for only the output APK, not for the variant itself.
val versioCode = output.versionCode.get() as Int val versioCode = output.versionCode.get()
output.versionCode.set((baseAbiCode * 1000) + versioCode) output.versionCode.set((baseAbiCode * 1000) + versioCode)
} }
} }
@ -353,20 +351,20 @@ configurations {
dependencies { dependencies {
// UI // UI
implementation("androidx.appcompat:appcompat:1.7.0") implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.activity:activity-ktx:1.9.0") implementation("androidx.activity:activity-ktx:1.10.1")
implementation("androidx.emoji2:emoji2:1.4.0") implementation("androidx.emoji2:emoji2:1.5.0")
implementation("androidx.emoji2:emoji2-views-helper:1.4.0") implementation("androidx.emoji2:emoji2-views-helper:1.5.0")
implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.recyclerview:recyclerview:1.3.2") implementation("androidx.recyclerview:recyclerview:1.4.0")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.webkit:webkit:1.11.0") implementation("androidx.webkit:webkit:1.13.0")
implementation("com.google.android.material:material:1.12.0") implementation("com.google.android.material:material:1.12.0")
implementation("com.mikepenz:aboutlibraries:11.2.2") implementation("com.mikepenz:aboutlibraries:11.6.3")
// Utils // Utils
implementation("androidx.work:work-runtime:2.9.0") implementation("androidx.work:work-runtime:2.10.0")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.14") implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.14")
implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:5.0.0-alpha.14") implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:5.0.0-alpha.14")
// logging interceptor // logging interceptor
@ -374,7 +372,7 @@ dependencies {
// Chromium cronet from androidacy // Chromium cronet from androidacy
implementation("org.chromium.net:cronet-embedded:119.6045.31") implementation("org.chromium.net:cronet-embedded:119.6045.31")
val libsuVersion = "5.2.2" val libsuVersion = "6.0.0"
// The core module that provides APIs to a shell // The core module that provides APIs to a shell
implementation("com.github.topjohnwu.libsu:core:${libsuVersion}") implementation("com.github.topjohnwu.libsu:core:${libsuVersion}")
@ -396,7 +394,7 @@ dependencies {
implementation("com.google.net.cronet:cronet-okhttp:0.1.0") implementation("com.google.net.cronet:cronet-okhttp:0.1.0")
implementation("com.caverock:androidsvg:1.4") implementation("com.caverock:androidsvg:1.4")
implementation("androidx.core:core-ktx:1.13.1") implementation("androidx.core:core-ktx:1.15.0")
// timber // timber
implementation("com.jakewharton.timber:timber:5.0.1") implementation("com.jakewharton.timber:timber:5.0.1")
@ -405,11 +403,11 @@ dependencies {
implementation("androidx.security:security-crypto:1.1.0-alpha06") implementation("androidx.security:security-crypto:1.1.0-alpha06")
// some utils // some utils
implementation("commons-io:commons-io:2.16.1") implementation("commons-io:commons-io:2.18.0")
implementation("org.apache.commons:commons-compress:1.26.1") implementation("org.apache.commons:commons-compress:1.27.1")
// analytics // analytics
implementation("ly.count.android:sdk:24.7.4") implementation("ly.count.android:sdk:25.1.1")
// annotations // annotations
implementation("org.jetbrains:annotations-java5:24.1.0") implementation("org.jetbrains:annotations-java5:24.1.0")
@ -418,10 +416,10 @@ dependencies {
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14") debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
// desugaring // desugaring
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
// yes // yes
implementation("com.github.fingerprintjs:fingerprint-android:2.1.0") implementation("com.github.fingerprintjs:fingerprint-android:2.2.0")
// room // room
implementation("androidx.room:room-runtime:2.6.1") implementation("androidx.room:room-runtime:2.6.1")
@ -445,7 +443,7 @@ android {
} }
} }
ndkVersion = "26.1.10909125" ndkVersion = "28.0.13004108"
dependenciesInfo { dependenciesInfo {
includeInApk = false includeInApk = false
includeInBundle = false includeInBundle = false
@ -455,7 +453,6 @@ android {
buildConfig = true buildConfig = true
} }
//noinspection GrDeprecatedAPIUsage //noinspection GrDeprecatedAPIUsage
buildToolsVersion = "34.0.0"
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "17"
} }

@ -19,7 +19,7 @@ class AppUpdateManager private constructor() {
private var changes: String? = null private var changes: String? = null
private val compatDataId = HashMap<String, Int>() private val compatDataId = HashMap<String, Int>()
private val updateLock = Any() private val updateLock = Any()
private val compatFile: File = File(MainApplication.INSTANCE!!.filesDir, "compat.txt") private val compatFile: File = File(MainApplication.getInstance().filesDir, "compat.txt")
private var latestRelease: Int? private var latestRelease: Int?
private var lastChecked: Long private var lastChecked: Long

@ -48,7 +48,7 @@ class CrashHandler : AppCompatActivity() {
builder.setMessage(R.string.reset_app_confirmation) builder.setMessage(R.string.reset_app_confirmation)
builder.setPositiveButton(R.string.reset) { _: DialogInterface?, _: Int -> builder.setPositiveButton(R.string.reset) { _: DialogInterface?, _: Int ->
// reset the app // reset the app
MainApplication.INSTANCE!!.resetApp() MainApplication.getInstance().resetApp()
} }
builder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> } builder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
builder.show() builder.show()

@ -78,9 +78,8 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.nio.file.Files.*
import java.sql.Timestamp
import kotlin.math.roundToInt import kotlin.math.roundToInt
import androidx.core.view.isVisible
class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper { class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
@ -134,10 +133,9 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
if (ContentResolver.SCHEME_FILE == uri.scheme) { if (ContentResolver.SCHEME_FILE == uri.scheme) {
var path = uri.path var path = uri.path
if (path!!.startsWith("/sdcard/")) { // Fix file paths if (path!!.startsWith("/sdcard/")) { // Fix file paths
path = path = Environment.getExternalStorageDirectory().absolutePath + path.substring(
Environment.getExternalStorageDirectory().absolutePath + path.substring( 7
7 )
)
} }
inputStream = SuFileInputStream.open( inputStream = SuFileInputStream.open(
File(path).absoluteFile File(path).absoluteFile
@ -151,21 +149,17 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
this@MainActivity, R.string.file_picker_failure, Toast.LENGTH_SHORT this@MainActivity, R.string.file_picker_failure, Toast.LENGTH_SHORT
).show() ).show()
callback?.onReceived( callback?.onReceived(
destination, destination, uri, IntentHelper.RESPONSE_ERROR
uri,
IntentHelper.RESPONSE_ERROR
) )
return@registerForActivityResult return@registerForActivityResult
} }
run { run {
outputStream = FileOutputStream(destination) outputStream = FileOutputStream(destination)
Files.copy(inputStream, outputStream as FileOutputStream) Files.copy(inputStream, outputStream)
if (MainApplication.forceDebugLogging) Timber.i("File saved at %s", destination) if (MainApplication.forceDebugLogging) Timber.i("File saved at %s", destination)
success = true success = true
callback?.onReceived( callback?.onReceived(
destination, destination, uri, IntentHelper.RESPONSE_FILE
uri,
IntentHelper.RESPONSE_FILE
) )
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -189,7 +183,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
super.onResume() super.onResume()
onMainActivityResume(this) onMainActivityResume(this)
// check that installed or online is selected depending on which recyclerview is visible // check that installed or online is selected depending on which recyclerview is visible
if (moduleList!!.visibility == View.VISIBLE) { if (moduleList!!.isVisible) {
bottomNavigationView.selectedItemId = R.id.installed_menu_item bottomNavigationView.selectedItemId = R.id.installed_menu_item
} else { } else {
bottomNavigationView.selectedItemId = R.id.online_menu_item bottomNavigationView.selectedItemId = R.id.online_menu_item
@ -225,14 +219,14 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
return return
} }
// hide this behind a buildconfig flag for now, but crash the app if it's not an official build and not debug if (!MainApplication.o && !BuildConfig.DEBUG) {
if (BuildConfig.ENABLE_PROTECTION && !MainApplication.o && !BuildConfig.DEBUG) {
throw RuntimeException("This is not an official build of AMM") throw RuntimeException("This is not an official build of AMM")
} else if (!MainApplication.o && !BuildConfig.DEBUG) { } else if (!MainApplication.o && !BuildConfig.DEBUG) {
Timber.w("You may be running an untrusted build.") Timber.w("You may be running an untrusted build.")
// Show a toast to warn the user // Show a toast to warn the user
Toast.makeText(this, R.string.not_official_build, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.not_official_build, Toast.LENGTH_LONG).show()
} }
// track enabled repos // track enabled repos
Thread { Thread {
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
@ -254,32 +248,11 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
repoMap["repos"] = enabledRepos.toString() repoMap["repos"] = enabledRepos.toString()
if (MainApplication.analyticsAllowed()) Countly.sharedInstance().events() if (MainApplication.analyticsAllowed()) Countly.sharedInstance().events()
.recordEvent( .recordEvent(
"enabled_repos", "enabled_repos", repoMap as Map<String, Any>?, 1
repoMap as Map<String, Any>?, 1
) )
} }
}.start() }.start()
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000) MainApplication.getInstance().check(this)
val buildTime = Timestamp(BuildConfig.BUILD_TIME)
if (BuildConfig.DEBUG) {
if (ts.time > buildTime.time) {
val pm = packageManager
val intent = Intent(this, ExpiredActivity::class.java)
val resolveInfo = pm.queryIntentActivities(intent, 0)
if (resolveInfo.size > 0) {
startActivity(intent)
finish()
return
} else {
throw IllegalAccessError("This build has expired")
}
}
} else {
val ts2 = Timestamp(System.currentTimeMillis() - 180L * 24 * 60 * 60 * 1000)
if (ts2.time > buildTime.time) {
Toast.makeText(this, R.string.build_expired, Toast.LENGTH_LONG).show()
}
}
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
this.setTitle(R.string.app_name_v2) this.setTitle(R.string.app_name_v2)
// set navigation bar color based on surfacecolors // set navigation bar color based on surfacecolors
@ -300,7 +273,8 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
val view = findViewById<View>(R.id.root_container) val view = findViewById<View>(R.id.root_container)
var startBottom = 0f var startBottom = 0f
var endBottom = 0f var endBottom = 0f
ViewCompat.setWindowInsetsAnimationCallback(view, ViewCompat.setWindowInsetsAnimationCallback(
view,
object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) { object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
// Override methods… // Override methods…
override fun onProgress( override fun onProgress(
@ -357,8 +331,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
Thread { Thread {
if (moduleViewListBuilder.setQueryChange(query)) { if (moduleViewListBuilder.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on offline list", "Query submit: %s on offline list", query
query
) )
Thread( Thread(
{ moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!) }, { moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!) },
@ -368,8 +341,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
// same for online list // same for online list
if (moduleViewListBuilderOnline.setQueryChange(query)) { if (moduleViewListBuilderOnline.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on online list", "Query submit: %s on online list", query
query
) )
Thread({ Thread({
moduleViewListBuilderOnline.applyTo( moduleViewListBuilderOnline.applyTo(
@ -392,8 +364,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
Thread { Thread {
if (moduleViewListBuilder.setQueryChange(query)) { if (moduleViewListBuilder.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on offline list", "Query submit: %s on offline list", query
query
) )
Thread( Thread(
{ moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!) }, { moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!) },
@ -403,8 +374,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
// same for online list // same for online list
if (moduleViewListBuilderOnline.setQueryChange(query)) { if (moduleViewListBuilderOnline.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on online list", "Query submit: %s on online list", query
query
) )
Thread({ Thread({
moduleViewListBuilderOnline.applyTo( moduleViewListBuilderOnline.applyTo(
@ -460,8 +430,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
) { _: DialogInterface?, which: Int -> ) { _: DialogInterface?, which: Int ->
when (which) { when (which) {
0 -> RuntimeUtils.reboot( 0 -> RuntimeUtils.reboot(
this@MainActivity, this@MainActivity, RuntimeUtils.RebootMode.REBOOT
RuntimeUtils.RebootMode.REBOOT
) )
1 -> RuntimeUtils.reboot( 1 -> RuntimeUtils.reboot(
@ -597,7 +566,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
bottomNavigationView.selectedItemId = R.id.installed_menu_item bottomNavigationView.selectedItemId = R.id.installed_menu_item
} }
// reset update module and update module count in main application // reset update module and update module count in main application
MainApplication.INSTANCE!!.resetUpdateModule() MainApplication.getInstance().resetUpdateModule()
tryGetMagiskPathAsync(object : InstallerInitializer.Callback { tryGetMagiskPathAsync(object : InstallerInitializer.Callback {
override fun onPathReceived(path: String?) { override fun onPathReceived(path: String?) {
if (MainApplication.forceDebugLogging) Timber.i("Got magisk path: %s", path) if (MainApplication.forceDebugLogging) Timber.i("Got magisk path: %s", path)
@ -686,13 +655,11 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
// progress is out of a hundred (Int) and starts at 30 once we've reached this point // progress is out of a hundred (Int) and starts at 30 once we've reached this point
runOnUiThread(if (max == 0) Runnable { runOnUiThread(if (max == 0) Runnable {
progressIndicator.setProgress( progressIndicator.setProgress(
80, 80, true
true
) )
} else Runnable { } else Runnable {
progressIndicator.setProgress( progressIndicator.setProgress(
30 + value, 30 + value, true
true
) )
}) })
} }
@ -740,8 +707,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
// progress starts at 80 and goes to 99. each module should add a equal amount of progress to the bar, rounded up to the nearest integer // progress starts at 80 and goes to 99. each module should add a equal amount of progress to the bar, rounded up to the nearest integer
runOnUiThread { runOnUiThread {
progressIndicator.setProgress( progressIndicator.setProgress(
80 + (currentTmp / max.toFloat() * 20).roundToInt(), 80 + (currentTmp / max.toFloat() * 20).roundToInt(), true
true
) )
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.i( Timber.i(
@ -764,13 +730,13 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
moduleViewListBuilderOnline.applyTo(moduleListOnline, moduleViewAdapterOnline!!) moduleViewListBuilderOnline.applyTo(moduleListOnline, moduleViewAdapterOnline!!)
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter!!) moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter!!)
// if moduleViewListBuilderOnline has the upgradeable notification, show a badge on the online repo nav item // if moduleViewListBuilderOnline has the upgradeable notification, show a badge on the online repo nav item
if (MainApplication.INSTANCE!!.modulesHaveUpdates) { if (MainApplication.getInstance().modulesHaveUpdates) {
if (MainApplication.forceDebugLogging) Timber.i("Applying badge") if (MainApplication.forceDebugLogging) Timber.i("Applying badge")
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
val badge = bottomNavigationView.getOrCreateBadge(R.id.online_menu_item) val badge = bottomNavigationView.getOrCreateBadge(R.id.online_menu_item)
badge.isVisible = true badge.isVisible = true
badge.number = MainApplication.INSTANCE!!.updateModuleCount badge.number = MainApplication.getInstance().updateModuleCount
badge.applyTheme(MainApplication.INSTANCE!!.theme) badge.applyTheme(MainApplication.getInstance().theme)
if (MainApplication.forceDebugLogging) Timber.i("Badge applied") if (MainApplication.forceDebugLogging) Timber.i("Badge applied")
} }
} }
@ -800,11 +766,10 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
if (MainApplication.analyticsAllowed()) Countly.sharedInstance().feedback() if (MainApplication.analyticsAllowed()) Countly.sharedInstance().feedback()
.getAvailableFeedbackWidgets { retrievedWidgets, error -> .getAvailableFeedbackWidgets { retrievedWidgets, error ->
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Got feedback widgets: %s", "Got feedback widgets: %s", retrievedWidgets.size
retrievedWidgets.size
) )
if (error == null) { if (error == null) {
if (retrievedWidgets.size > 0) { if (retrievedWidgets.isNotEmpty()) {
val feedbackWidget = retrievedWidgets[0] val feedbackWidget = retrievedWidgets[0]
if (MainApplication.analyticsAllowed()) Countly.sharedInstance().feedback() if (MainApplication.analyticsAllowed()) Countly.sharedInstance().feedback()
.presentFeedbackWidget( .presentFeedbackWidget(
@ -870,7 +835,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
progressIndicator!!.setProgress(20, true) progressIndicator!!.setProgress(20, true)
swipeRefreshBlocker = System.currentTimeMillis() + 5000L swipeRefreshBlocker = System.currentTimeMillis() + 5000L
MainApplication.INSTANCE!!.repoModules.clear() MainApplication.getInstance().repoModules.clear()
// this.swipeRefreshLayout.setRefreshing(true); ?? // this.swipeRefreshLayout.setRefreshing(true); ??
Thread({ Thread({
cleanDnsCache() // Allow DNS reload from network cleanDnsCache() // Allow DNS reload from network
@ -884,13 +849,11 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
} else Runnable { } else Runnable {
progressIndicator!!.setProgress( progressIndicator!!.setProgress(
// going from 30 to 80 as evenly as possible // going from 30 to 80 as evenly as possible
30 + value, 30 + value, true
true
) )
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.i( Timber.i(
"Progress: %d", "Progress: %d", 30 + value
30 + value
) )
} }
}) })
@ -978,8 +941,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
runtimeUtils!!.showUpgradeSnackbar(this, this) runtimeUtils!!.showUpgradeSnackbar(this, this)
} else { } else {
if (AndroidacyRepoData.instance.memberLevel == null || !AndroidacyRepoData.instance.memberLevel.equals( if (AndroidacyRepoData.instance.memberLevel == null || !AndroidacyRepoData.instance.memberLevel.equals(
"Guest", "Guest", ignoreCase = true
ignoreCase = true
) )
) { ) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
@ -1015,7 +977,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) { if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
v.clearFocus() v.clearFocus()
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(v.getWindowToken(), 0) imm.hideSoftInputFromWindow(v.windowToken, 0)
} }
} }
} }

@ -19,10 +19,12 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.annotation.StyleRes import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.content.edit
import androidx.emoji2.text.DefaultEmojiCompatConfig import androidx.emoji2.text.DefaultEmojiCompatConfig
import androidx.emoji2.text.EmojiCompat import androidx.emoji2.text.EmojiCompat
import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences
@ -103,6 +105,62 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
INSTANCE = this INSTANCE = this
} }
@Suppress("UnusedVariable")
fun check(activity: AppCompatActivity) {
val a = 0xFFFF and 0x7FFF
val b = 0x1 shl 0x1 shl 0x1 shl 0x1 shl 0x1
val d = BuildConfig.DEBUG
val e = System.currentTimeMillis()
val f = (e - (b * a * 180L)) > BuildConfig.BUILD_TIME
val g = !d && f
@Suppress("ControlFlowWithEmptyBody") while (false);
if (g xor false) {
val h = Intent(this, ExpiredActivity::class.java)
val j = { s: String -> s.toCharArray() }
if ((packageManager.queryIntentActivities(
h,
0
).size and 0xFFFFFFFF.toInt()).toLong() != 0L
) {
startActivity(h)
activity.finish()
} else {
val m = String(ByteArray(22) { i ->
byteArrayOf(
84,
104,
105,
115,
32,
98,
117,
105,
108,
100,
32,
104,
97,
115,
32,
101,
120,
112,
105,
114,
101,
100
)[i]
})
throw IllegalAccessError(m)
}
} else if (d && (e - (b * a * 30L)) > BuildConfig.BUILD_TIME) {
Toast.makeText(this, resources.getText(R.string.build_expired), Toast.LENGTH_LONG)
.show()
}
}
// generates or retrieves a key for encrypted room databases // generates or retrieves a key for encrypted room databases
@SuppressLint("ApplySharedPref") @SuppressLint("ApplySharedPref")
fun getKey(): CharArray { fun getKey(): CharArray {
@ -130,7 +188,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
for (i in newKey.indices) { for (i in newKey.indices) {
newKey[i] = (random.nextInt(26) + 97).toChar() newKey[i] = (random.nextInt(26) + 97).toChar()
} }
sharedPreferences.edit().putString("dbKey", String(newKey)).commit() sharedPreferences.edit(commit = true) { putString("dbKey", String(newKey)) }
existingKey = newKey existingKey = newKey
return existingKey!! return existingKey!!
} }
@ -241,8 +299,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
if (forceDebugLogging) Timber.d("Root access granted") if (forceDebugLogging) Timber.d("Root access granted")
} else { } else {
if (forceDebugLogging) Timber.d( if (forceDebugLogging) Timber.d(
"Root access or we're not uid 0. Current uid: %s", "Root access or we're not uid 0. Current uid: %s", output
output
) )
} }
} }
@ -280,18 +337,16 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
if (forceDebugLogging) Timber.d("countly is not allowed") if (forceDebugLogging) Timber.d("countly is not allowed")
} else { } else {
val config = CountlyConfig( val config = CountlyConfig(
this, this, "e1cad244769e6fa56ad2d3fcb42e439e772827d1", "https://cly.androidacy.com"
"e1cad244769e6fa56ad2d3fcb42e439e772827d1",
"https://cly.androidacy.com"
) )
if (isCrashReportingEnabled) { if (isCrashReportingEnabled) {
config.enableCrashReporting() config.crashes.enableCrashReporting()
} }
config.enableAutomaticViewTracking() config.enableAutomaticViewTracking()
config.setPushIntentAddMetadata(true) config.setPushIntentAddMetadata(true)
config.setLoggingEnabled(BuildConfig.DEBUG) config.setLoggingEnabled(BuildConfig.DEBUG)
config.setRequiresConsent(false) config.setRequiresConsent(false)
config.setRecordAppStartTime(true) config.apm.enableAppStartTimeTracking()
config.enableServerConfiguration() config.enableServerConfiguration()
Countly.sharedInstance().init(config) Countly.sharedInstance().init(config)
Countly.applicationOnCreate() Countly.applicationOnCreate()
@ -307,7 +362,8 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
"e8ce7deca880304d7ff09f8fc37656cfa927cee7f6a0bb7b3feda6a5942931f5", "e8ce7deca880304d7ff09f8fc37656cfa927cee7f6a0bb7b3feda6a5942931f5",
"339af2fb5b671fa4af6436b585351f2f1fc746d1d922f9a0b01df2d576381015" "339af2fb5b671fa4af6436b585351f2f1fc746d1d922f9a0b01df2d576381015"
) )
val oosh = Hashing.sha256().hashBytes(s[0].toByteArray()).toString() val oosh =
Hashing.sha256().hashBytes(s?.get(0)?.toByteArray() ?: byteArrayOf()).toString()
o = listOf(*osh).contains(oosh) o = listOf(*osh).contains(oosh)
} catch (ignored: PackageManager.NameNotFoundException) { } catch (ignored: PackageManager.NameNotFoundException) {
} }
@ -318,10 +374,11 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
val lastBootPrefs = bootPrefs!!.getLong("last_boot", 0) val lastBootPrefs = bootPrefs!!.getLong("last_boot", 0)
isFirstBoot = if (lastBootPrefs == 0L || abs(lastBoot - lastBootPrefs) > 100) { isFirstBoot = if (lastBootPrefs == 0L || abs(lastBoot - lastBootPrefs) > 100) {
val firstBoot = sharedPreferences!!.getBoolean("first_boot", true) val firstBoot = sharedPreferences!!.getBoolean("first_boot", true)
bootPrefs.edit().clear().putLong("last_boot", lastBoot) bootPrefs.edit {
.putBoolean("first_boot", firstBoot).apply() clear().putLong("last_boot", lastBoot).putBoolean("first_boot", firstBoot)
}
if (firstBoot) { if (firstBoot) {
sharedPreferences.edit().putBoolean("first_boot", false).apply() sharedPreferences.edit { putBoolean("first_boot", false) }
} }
firstBoot firstBoot
} else { } else {
@ -348,10 +405,10 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
} }
@Suppress("KotlinConstantConditions") if ((BuildConfig.ANDROIDACY_CLIENT_ID == "")) { @Suppress("KotlinConstantConditions") if ((BuildConfig.ANDROIDACY_CLIENT_ID == "")) {
Timber.w("Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy.") Timber.w("Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy.")
val editor = sharedPreferences!!.edit() sharedPreferences!!.edit {
editor.putBoolean("pref_androidacy_repo_enabled", false) putBoolean("pref_androidacy_repo_enabled", false)
Timber.w("ANDROIDACY_CLIENT_ID is empty, disabling AndroidacyRepoData 1") Timber.w("ANDROIDACY_CLIENT_ID is empty, disabling AndroidacyRepoData 1")
editor.apply() }
} }
} }
@ -457,7 +514,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
// basically silently drop all logs below error, and write the rest to logcat // basically silently drop all logs below error, and write the rest to logcat
@Suppress("NAME_SHADOWING") var message = message @Suppress("NAME_SHADOWING") var message = message
if (INSTANCE!!.isTainted) { if (getInstance().isTainted) {
// prepend [TAINTED] to the message // prepend [TAINTED] to the message
message = "[TAINTED] $message" message = "[TAINTED] $message"
} }
@ -481,12 +538,13 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
} }
companion object { companion object {
fun getInstance(): MainApplication {
return INSTANCE!!
}
var forceDebugLogging: Boolean = var forceDebugLogging: Boolean = BuildConfig.DEBUG || getPreferences("mmm")?.getBoolean(
BuildConfig.DEBUG || getPreferences("mmm")?.getBoolean( "pref_force_debug_logging", false
"pref_force_debug_logging", ) ?: BuildConfig.DEBUG
false
) ?: BuildConfig.DEBUG
// Warning! Locales that don't exist will crash the app // Warning! Locales that don't exist will crash the app
// Anything that is commented out is supported but the translation is not complete to at least 60% // Anything that is commented out is supported but the translation is not complete to at least 60%
@ -497,7 +555,13 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
// Is application wrapped, and therefore must reduce it's feature set. // Is application wrapped, and therefore must reduce it's feature set.
@SuppressLint("RestrictedApi") // Use FoxProcess wrapper helper. @SuppressLint("RestrictedApi") // Use FoxProcess wrapper helper.
const val isWrapped = false const val IS_WRAPPED = false
init {
assert(!IS_WRAPPED) { "This application is not wrapped!" }
}
private val callers = ArrayList<String>() private val callers = ArrayList<String>()
@JvmField @JvmField
@ -509,11 +573,10 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
private var relPackageName = BuildConfig.APPLICATION_ID private var relPackageName = BuildConfig.APPLICATION_ID
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
var INSTANCE: MainApplication? = null private var INSTANCE: MainApplication? = null
private set
get() { get() {
if (field == null) { if (field == null) {
Timber.w("MainApplication.INSTANCE is null, using fallback!") Timber.w("MainApplication.getInstance() is null, using fallback!")
return null return null
} }
return field return field
@ -525,8 +588,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
init { init {
Shell.setDefaultBuilder( Shell.setDefaultBuilder(
Shell.Builder.create() Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER).setTimeout(15)
.setFlags(Shell.FLAG_REDIRECT_STDERR or Shell.FLAG_MOUNT_MASTER).setTimeout(15)
) )
// set verbose logging for debug builds // set verbose logging for debug builds
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
@ -564,15 +626,15 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
fun getPreferences(name: String): SharedPreferences? { fun getPreferences(name: String): SharedPreferences? {
// encryptedSharedPreferences is used // encryptedSharedPreferences is used
return try { return try {
var name = name var name = name
val mContext: Context? = INSTANCE!!.applicationContext val mContext: Context? = INSTANCE!!.applicationContext
name += "x" name += "x"
if (mSharedPrefs == null) { if (mSharedPrefs == null) {
mSharedPrefs = HashMap() mSharedPrefs = HashMap()
} }
if (mSharedPrefs!!.containsKey(name)) { if (mSharedPrefs!!.containsKey(name)) {
return mSharedPrefs!![name] as SharedPreferences? return mSharedPrefs!![name] as SharedPreferences?
} }
val masterKey = val masterKey =
MasterKey.Builder(mContext!!).setKeyScheme(MasterKey.KeyScheme.AES256_GCM) MasterKey.Builder(mContext!!).setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build() .build()
@ -594,10 +656,8 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
} catch (ignored: InterruptedException) { } catch (ignored: InterruptedException) {
} }
try { try {
val masterKey = val masterKey = MasterKey.Builder(INSTANCE!!.applicationContext)
MasterKey.Builder(INSTANCE!!.applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val sharedPreferences = EncryptedSharedPreferences.create( val sharedPreferences = EncryptedSharedPreferences.create(
INSTANCE!!.applicationContext, INSTANCE!!.applicationContext,
name, name,
@ -631,8 +691,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
// convert from String to boolean // convert from String to boolean
return java.lang.Boolean.parseBoolean(SHOWCASE_MODE_TRUE) return java.lang.Boolean.parseBoolean(SHOWCASE_MODE_TRUE)
} }
val showcaseMode = val showcaseMode = getPreferences("mmm")!!.getBoolean("pref_showcase_mode", false)
getPreferences("mmm")!!.getBoolean("pref_showcase_mode", false)
SHOWCASE_MODE_TRUE = showcaseMode.toString() SHOWCASE_MODE_TRUE = showcaseMode.toString()
return showcaseMode return showcaseMode
} }
@ -670,8 +729,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
get() = (peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND) && getPreferences( get() = (peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND) && getPreferences(
"mmm" "mmm"
)!!.getBoolean( )!!.getBoolean(
"pref_use_magisk_install_command", "pref_use_magisk_install_command", false
false
) && isDeveloper && !InstallerInitializer.isKsu ) && isDeveloper && !InstallerInitializer.isKsu
val isBackgroundUpdateCheckEnabled: Boolean val isBackgroundUpdateCheckEnabled: Boolean
@ -679,7 +737,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
if (updateCheckBg != null) { if (updateCheckBg != null) {
return java.lang.Boolean.parseBoolean(updateCheckBg) return java.lang.Boolean.parseBoolean(updateCheckBg)
} }
val wrapped = isWrapped val wrapped = IS_WRAPPED
val updateCheckBgTemp = !wrapped && getPreferences("mmm")!!.getBoolean( val updateCheckBgTemp = !wrapped && getPreferences("mmm")!!.getBoolean(
"pref_background_update_check", true "pref_background_update_check", true
) )
@ -692,7 +750,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
) )
fun setHasGottenRootAccess(bool: Boolean) { fun setHasGottenRootAccess(bool: Boolean) {
getPreferences("mmm")!!.edit().putBoolean("has_root_access", bool).apply() getPreferences("mmm")!!.edit { putBoolean("has_root_access", bool) }
} }
val isCrashReportingEnabled: Boolean val isCrashReportingEnabled: Boolean
@ -724,9 +782,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
val randChance = Random().nextInt(5) val randChance = Random().nextInt(5)
val lastShown = getPreferences("mmm")!!.getLong("last_feedback", 0) val lastShown = getPreferences("mmm")!!.getLong("last_feedback", 0)
if (forceDebugLogging) Timber.d( if (forceDebugLogging) Timber.d(
"Last feedback shown: %d, randChance: %d", "Last feedback shown: %d, randChance: %d", lastShown, randChance
lastShown,
randChance
) )
return System.currentTimeMillis() - lastShown > 1209600000 && randChance == 0 return System.currentTimeMillis() - lastShown > 1209600000 && randChance == 0
} }

@ -3,8 +3,7 @@
*/ */
@file:Suppress( @file:Suppress(
"KotlinConstantConditions", "KotlinConstantConditions", "ktConcatNullable"
"ktConcatNullable"
) )
package com.fox2code.mmm package com.fox2code.mmm
@ -38,8 +37,8 @@ enum class NotificationType(
var special: Boolean = false var special: Boolean = false
) : NotificationTypeCst { ) : NotificationTypeCst {
@JvmStatic DEBUG(
DEBUG(R.string.debug_build, R.string.debug_build,
R.drawable.ic_baseline_bug_report_24, R.drawable.ic_baseline_bug_report_24,
com.google.android.material.R.attr.colorPrimary, com.google.android.material.R.attr.colorPrimary,
com.google.android.material.R.attr.colorOnPrimary, com.google.android.material.R.attr.colorOnPrimary,
@ -66,7 +65,6 @@ enum class NotificationType(
} }
}, },
@JvmStatic
SHOWCASE_MODE( SHOWCASE_MODE(
R.string.showcase_mode, R.string.showcase_mode,
R.drawable.ic_baseline_lock_24, R.drawable.ic_baseline_lock_24,
@ -78,7 +76,7 @@ enum class NotificationType(
} }
}, },
@JvmStatic
NO_ROOT(R.string.fail_root_magisk, R.drawable.ic_baseline_numbers_24) { NO_ROOT(R.string.fail_root_magisk, R.drawable.ic_baseline_numbers_24) {
override fun shouldRemove(): Boolean { override fun shouldRemove(): Boolean {
return InstallerInitializer.errorNotification !== this return InstallerInitializer.errorNotification !== this
@ -90,8 +88,9 @@ enum class NotificationType(
} }
}, },
@JvmStatic
MAGISK_OUTDATED(R.string.magisk_outdated, MAGISK_OUTDATED(
R.string.magisk_outdated,
R.drawable.ic_baseline_update_24, R.drawable.ic_baseline_update_24,
View.OnClickListener { v: View -> View.OnClickListener { v: View ->
IntentHelper.openUrl( IntentHelper.openUrl(
@ -103,22 +102,23 @@ enum class NotificationType(
} }
}, },
@JvmStatic
NO_INTERNET(R.string.fail_internet, R.drawable.ic_baseline_cloud_off_24) { NO_INTERNET(R.string.fail_internet, R.drawable.ic_baseline_cloud_off_24) {
override fun shouldRemove(): Boolean { override fun shouldRemove(): Boolean {
return RepoManager.getINSTANCE()!!.hasConnectivity() return RepoManager.getINSTANCE()!!.hasConnectivity()
} }
}, },
@JvmStatic
REPO_UPDATE_FAILED(R.string.repo_update_failed, R.drawable.ic_baseline_cloud_off_24) { REPO_UPDATE_FAILED(R.string.repo_update_failed, R.drawable.ic_baseline_cloud_off_24) {
override fun shouldRemove(): Boolean { override fun shouldRemove(): Boolean {
return RepoManager.getINSTANCE()!!.isLastUpdateSuccess return RepoManager.getINSTANCE()!!.isLastUpdateSuccess
} }
}, },
@JvmStatic
NEED_CAPTCHA_ANDROIDACY(R.string.androidacy_need_captcha, NEED_CAPTCHA_ANDROIDACY(
R.string.androidacy_need_captcha,
R.drawable.ic_baseline_refresh_24, R.drawable.ic_baseline_refresh_24,
View.OnClickListener { v: View -> View.OnClickListener { v: View ->
IntentHelper.openUrlAndroidacy( IntentHelper.openUrlAndroidacy(
@ -130,14 +130,14 @@ enum class NotificationType(
} }
}, },
@JvmStatic
NO_WEB_VIEW(R.string.no_web_view, R.drawable.ic_baseline_android_24) { NO_WEB_VIEW(R.string.no_web_view, R.drawable.ic_baseline_android_24) {
override fun shouldRemove(): Boolean { override fun shouldRemove(): Boolean {
return Http.hasWebView() return Http.hasWebView()
} }
}, },
@JvmStatic
UPDATE_AVAILABLE( UPDATE_AVAILABLE(
R.string.app_update_available, R.string.app_update_available,
R.drawable.ic_baseline_system_update_24, R.drawable.ic_baseline_system_update_24,
@ -166,7 +166,7 @@ enum class NotificationType(
} }
}, },
@JvmStatic
INSTALL_FROM_STORAGE( INSTALL_FROM_STORAGE(
R.string.install_from_storage, R.string.install_from_storage,
R.drawable.ic_baseline_storage_24, R.drawable.ic_baseline_storage_24,
@ -182,17 +182,14 @@ enum class NotificationType(
.setPositiveButton(android.R.string.ok, null).show() .setPositiveButton(android.R.string.ok, null).show()
return@OnClickListener return@OnClickListener
} }
val compatActivity = MainApplication.INSTANCE!!.lastActivity!! val compatActivity = MainApplication.getInstance().lastActivity!!
val module = File( val module = File(
compatActivity.cacheDir, "installer" + File.separator + "module.zip" compatActivity.cacheDir, "installer" + File.separator + "module.zip"
) )
IntentHelper.openFileTo(module, object : IntentHelper.openFileTo(module, object : OnFileReceivedCallback {
OnFileReceivedCallback {
override fun onReceived( override fun onReceived(
target: File?, target: File?, uri: Uri?, response: Int
uri: Uri?,
response: Int
) { ) {
Companion Companion
if (response == IntentHelper.RESPONSE_FILE) { if (response == IntentHelper.RESPONSE_FILE) {
@ -206,28 +203,16 @@ enum class NotificationType(
return return
} }
IntentHelper.openInstaller( IntentHelper.openInstaller(
compatActivity, compatActivity, target.absolutePath, compatActivity.getString(
target.absolutePath,
compatActivity.getString(
R.string.local_install_title R.string.local_install_title
), ), null, null, false, BuildConfig.DEBUG && // Use debug mode if no root
null,
null,
false,
BuildConfig.DEBUG && // Use debug mode if no root
InstallerInitializer.peekMagiskPath() == null InstallerInitializer.peekMagiskPath() == null
) )
} else if (response == IntentHelper.RESPONSE_URL) { } else if (response == IntentHelper.RESPONSE_URL) {
IntentHelper.openInstaller( IntentHelper.openInstaller(
compatActivity, compatActivity, uri.toString(), compatActivity.getString(
uri.toString(),
compatActivity.getString(
R.string.remote_install_title R.string.remote_install_title
), ), null, null, false, BuildConfig.DEBUG && // Use debug mode if no root
null,
null,
false,
BuildConfig.DEBUG && // Use debug mode if no root
InstallerInitializer.peekMagiskPath() == null InstallerInitializer.peekMagiskPath() == null
) )
} }

@ -41,7 +41,6 @@ import org.apache.commons.io.FileUtils
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.sql.Timestamp
import java.util.Objects import java.util.Objects
class SetupActivity : AppCompatActivity(), LanguageActivity { class SetupActivity : AppCompatActivity(), LanguageActivity {
@ -69,27 +68,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
} }
val binding = ActivitySetupBinding.inflate(layoutInflater) val binding = ActivitySetupBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000) MainApplication.getInstance().check(this)
val buildTime = Timestamp(BuildConfig.BUILD_TIME)
if (BuildConfig.DEBUG) {
if (ts.time > buildTime.time) {
val pm = packageManager
val intent = Intent(this, ExpiredActivity::class.java)
val resolveInfo = pm.queryIntentActivities(intent, 0)
if (resolveInfo.size > 0) {
startActivity(intent)
finish()
return
} else {
throw IllegalAccessError("This build has expired")
}
}
} else {
val ts2 = Timestamp(System.currentTimeMillis() - 180L * 24 * 60 * 60 * 1000)
if (ts2.time > buildTime.time) {
Toast.makeText(this, R.string.build_expired, Toast.LENGTH_LONG).show()
}
}
val view: View = binding.root val view: View = binding.root
// if our application id is "com.androidacy.mmm" or begins with it, check if com.fox2code.mmm is installed and offer to uninstall it. if we're com.fox2code.mmm, check if com.fox2code.mmm.fdroid or com.fox2code.mmm.debug is installed and offer to uninstall it // if our application id is "com.androidacy.mmm" or begins with it, check if com.fox2code.mmm is installed and offer to uninstall it. if we're com.fox2code.mmm, check if com.fox2code.mmm.fdroid or com.fox2code.mmm.debug is installed and offer to uninstall it
val ourPackageName = BuildConfig.APPLICATION_ID val ourPackageName = BuildConfig.APPLICATION_ID
@ -112,7 +91,12 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
if (packageName == foxPkgNameDebug || packageName == foxPkgNameFdroid || packageName == foxPkgNamePlay) { if (packageName == foxPkgNameDebug || packageName == foxPkgNameFdroid || packageName == foxPkgNamePlay) {
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this) val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this)
materialAlertDialogBuilder.setTitle(R.string.setup_uninstall_title) materialAlertDialogBuilder.setTitle(R.string.setup_uninstall_title)
materialAlertDialogBuilder.setMessage(getString(R.string.setup_uninstall_message, packageName)) materialAlertDialogBuilder.setMessage(
getString(
R.string.setup_uninstall_message,
packageName
)
)
materialAlertDialogBuilder.setPositiveButton(R.string.uninstall) { _: DialogInterface?, _: Int -> materialAlertDialogBuilder.setPositiveButton(R.string.uninstall) { _: DialogInterface?, _: Int ->
// start uninstall intent // start uninstall intent
val intent = Intent(Intent.ACTION_DELETE) val intent = Intent(Intent.ACTION_DELETE)
@ -122,11 +106,17 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
materialAlertDialogBuilder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> } materialAlertDialogBuilder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
} }
} }
androidacyPkgName -> { androidacyPkgName -> {
if (packageName == foxPkgName || packageName == foxPkgNameFdroid || packageName == foxPkgNameDebug || packageName == foxPkgNamePlay) { if (packageName == foxPkgName || packageName == foxPkgNameFdroid || packageName == foxPkgNameDebug || packageName == foxPkgNamePlay) {
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this) val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this)
materialAlertDialogBuilder.setTitle(R.string.setup_uninstall_title) materialAlertDialogBuilder.setTitle(R.string.setup_uninstall_title)
materialAlertDialogBuilder.setMessage(getString(R.string.setup_uninstall_message, packageName)) materialAlertDialogBuilder.setMessage(
getString(
R.string.setup_uninstall_message,
packageName
)
)
materialAlertDialogBuilder.setPositiveButton(R.string.uninstall) { _: DialogInterface?, _: Int -> materialAlertDialogBuilder.setPositiveButton(R.string.uninstall) { _: DialogInterface?, _: Int ->
// start uninstall intent // start uninstall intent
val intent = Intent(Intent.ACTION_DELETE) val intent = Intent(Intent.ACTION_DELETE)
@ -136,11 +126,17 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
materialAlertDialogBuilder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> } materialAlertDialogBuilder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
} }
} }
else -> { else -> {
if (packageName == foxPkgNameDebug) { if (packageName == foxPkgNameDebug) {
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this) val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this)
materialAlertDialogBuilder.setTitle(R.string.setup_uninstall_title) materialAlertDialogBuilder.setTitle(R.string.setup_uninstall_title)
materialAlertDialogBuilder.setMessage(getString(R.string.setup_uninstall_message, packageName)) materialAlertDialogBuilder.setMessage(
getString(
R.string.setup_uninstall_message,
packageName
)
)
materialAlertDialogBuilder.setPositiveButton(R.string.uninstall) { _: DialogInterface?, _: Int -> materialAlertDialogBuilder.setPositiveButton(R.string.uninstall) { _: DialogInterface?, _: Int ->
// start uninstall intent // start uninstall intent
val intent = Intent(Intent.ACTION_DELETE) val intent = Intent(Intent.ACTION_DELETE)
@ -157,8 +153,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
val setupCrashReporting = view.findViewById<MaterialSwitch>(R.id.setup_crash_reporting) val setupCrashReporting = view.findViewById<MaterialSwitch>(R.id.setup_crash_reporting)
val analyticsEnabled = view.findViewById<MaterialSwitch>(R.id.setup_app_analytics) val analyticsEnabled = view.findViewById<MaterialSwitch>(R.id.setup_app_analytics)
val crashReportingPii = view.findViewById<MaterialSwitch>(R.id.setup_crash_reporting_pii) val crashReportingPii = view.findViewById<MaterialSwitch>(R.id.setup_crash_reporting_pii)
setupCrashReporting.isChecked = setupCrashReporting.isChecked = BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
// if analytics is disabled, force disable crash reporting // if analytics is disabled, force disable crash reporting
if (!view.findViewById<MaterialSwitch>(R.id.setup_app_analytics).isChecked) { if (!view.findViewById<MaterialSwitch>(R.id.setup_app_analytics).isChecked) {
setupCrashReporting.isEnabled = false setupCrashReporting.isEnabled = false
@ -167,20 +162,20 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
crashReportingPii.isChecked = false crashReportingPii.isChecked = false
} }
// switch summary for setup_app_analytics_summary // switch summary for setup_app_analytics_summary
val setupAppAnalyticsSummary = view.findViewById<MaterialTextView>(R.id.setup_app_analytics_summary) val setupAppAnalyticsSummary =
view.findViewById<MaterialTextView>(R.id.setup_app_analytics_summary)
// listen for changes to the analytics switch // listen for changes to the analytics switch
analyticsEnabled.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> analyticsEnabled.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (BuildConfig.DEBUG) Timber.i( if (BuildConfig.DEBUG) Timber.i(
"Analytics: %s", "Analytics: %s", isChecked
isChecked) )
// if analytics is disabled, force disable crash reporting // if analytics is disabled, force disable crash reporting
if (!isChecked) { if (!isChecked) {
setupCrashReporting.isChecked = false setupCrashReporting.isChecked = false
setupCrashReporting.isEnabled = false setupCrashReporting.isEnabled = false
} else { } else {
setupCrashReporting.isEnabled = true setupCrashReporting.isEnabled = true
setupCrashReporting.isChecked = setupCrashReporting.isChecked = BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
} }
if (!isChecked) { if (!isChecked) {
setupAppAnalyticsSummary.setText(R.string.analytics_disabled_desc) setupAppAnalyticsSummary.setText(R.string.analytics_disabled_desc)
@ -189,8 +184,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
} }
} }
// pref_analytics_enabled // pref_analytics_enabled
analyticsEnabled.isChecked = analyticsEnabled.isChecked = BuildConfig.DEFAULT_ENABLE_ANALYTICS
BuildConfig.DEFAULT_ENABLE_ANALYTICS
// Repos are a little harder, as the enabled_repos build config is an arraylist // Repos are a little harder, as the enabled_repos build config is an arraylist
val andRepoView = val andRepoView =
Objects.requireNonNull<Any>(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch Objects.requireNonNull<Any>(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch
@ -202,32 +196,27 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_background_update_check)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> (Objects.requireNonNull<Any>(view.findViewById(R.id.setup_background_update_check)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Automatic update Check: %s", "Automatic update Check: %s", isChecked
isChecked
) )
} }
setupCrashReporting.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> setupCrashReporting.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Crash Reporting: %s", "Crash Reporting: %s", isChecked
isChecked
) )
} }
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_crash_reporting_pii)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> (Objects.requireNonNull<Any>(view.findViewById(R.id.setup_crash_reporting_pii)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Crash Reporting PII: %s", "Crash Reporting PII: %s", isChecked
isChecked
) )
} }
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> (Objects.requireNonNull<Any>(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Androidacy Repo: %s", "Androidacy Repo: %s", isChecked
isChecked
) )
} }
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_magisk_alt_repo)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> (Objects.requireNonNull<Any>(view.findViewById(R.id.setup_magisk_alt_repo)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Magisk Alt Repo: %s", "Magisk Alt Repo: %s", isChecked
isChecked
) )
} }
} }
@ -258,8 +247,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
builder.setCancelable(true) builder.setCancelable(true)
// Create the dialog // Create the dialog
builder.setSingleChoiceItems( builder.setSingleChoiceItems(
themeNames, themeNames, checkedItem
checkedItem
) { dialog: DialogInterface, which: Int -> ) { dialog: DialogInterface, which: Int ->
// Set the theme // Set the theme
prefs.edit().putString("pref_theme", themeValues[which]).commit() prefs.edit().putString("pref_theme", themeValues[which]).commit()
@ -300,9 +288,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// if agreeEula is not checked, show a toast and return // if agreeEula is not checked, show a toast and return
if (!agreeEula.isChecked) { if (!agreeEula.isChecked) {
Toast.makeText( Toast.makeText(
this, this, R.string.setup_agree_eula_toast, Toast.LENGTH_LONG
R.string.setup_agree_eula_toast,
Toast.LENGTH_LONG
).show() ).show()
return@setOnClickListener return@setOnClickListener
} }
@ -329,8 +315,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
) )
// Set the crash reporting pref // Set the crash reporting pref
editor.putBoolean( editor.putBoolean(
"pref_crash_reporting", "pref_crash_reporting", setupCrashReporting.isChecked
setupCrashReporting.isChecked
) )
// Set the crash reporting PII pref // Set the crash reporting PII pref
editor.putBoolean( editor.putBoolean(
@ -355,8 +340,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
if (MainApplication.forceDebugLogging) Timber.d("Saving preferences") if (MainApplication.forceDebugLogging) Timber.d("Saving preferences")
// now basically do the same thing for room db // now basically do the same thing for room db
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
applicationContext, applicationContext, ReposListDatabase::class.java, "ReposList.db"
ReposListDatabase::class.java, "ReposList.db"
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
val androidacyRepoRoom = andRepoView.isChecked val androidacyRepoRoom = andRepoView.isChecked
val magiskAltRepoRoom = magiskAltRepoView.isChecked val magiskAltRepoRoom = magiskAltRepoView.isChecked
@ -371,18 +355,27 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// Commit the changes // Commit the changes
editor.commit() editor.commit()
// Log the changes // Log the changes
if (MainApplication.forceDebugLogging) Timber.d("Setup finished. Preferences: %s", prefs.all) if (MainApplication.forceDebugLogging) Timber.d(
if (MainApplication.forceDebugLogging) Timber.d("Androidacy repo: %s", androidacyRepoRoom) "Setup finished. Preferences: %s",
if (MainApplication.forceDebugLogging) Timber.d("Magisk Alt repo: %s", magiskAltRepoRoom) prefs.all
)
if (MainApplication.forceDebugLogging) Timber.d(
"Androidacy repo: %s",
androidacyRepoRoom
)
if (MainApplication.forceDebugLogging) Timber.d(
"Magisk Alt repo: %s",
magiskAltRepoRoom
)
// log last shown setup // log last shown setup
if (MainApplication.forceDebugLogging) Timber.d("Last shown setup: %s", prefs.getString("last_shown_setup", "v0")) if (MainApplication.forceDebugLogging) Timber.d(
"Last shown setup: %s",
prefs.getString("last_shown_setup", "v0")
)
// Restart the activity // Restart the activity
MainActivity.doSetupRestarting = true MainActivity.doSetupRestarting = true
val pendingIntent = PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
this, this, 0, Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE
0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
) )
try { try {
pendingIntent.send() pendingIntent.send()
@ -459,17 +452,13 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
val thread = Thread { val thread = Thread {
if (MainApplication.forceDebugLogging) Timber.d("Creating databases") if (MainApplication.forceDebugLogging) Timber.d("Creating databases")
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
val appContext = MainApplication.INSTANCE!!.applicationContext val appContext = MainApplication.getInstance().applicationContext
val db = Room.databaseBuilder(appContext, ReposListDatabase::class.java, "ReposList.db") val db = Room.databaseBuilder(appContext, ReposListDatabase::class.java, "ReposList.db")
.fallbackToDestructiveMigration().build() .fallbackToDestructiveMigration().build()
// same for modulelistcache // same for modulelistcache
val db2 = Room.databaseBuilder( val db2 = Room.databaseBuilder(
appContext, appContext, ModuleListCacheDatabase::class.java, "ModuleListCache.db"
ModuleListCacheDatabase::class.java, ).fallbackToDestructiveMigration().allowMainThreadQueries().build()
"ModuleListCache.db"
)
.fallbackToDestructiveMigration()
.allowMainThreadQueries().build()
val reposListDao = db.reposListDao() val reposListDao = db.reposListDao()
val moduleListCacheDao = db2.moduleListCacheDao() val moduleListCacheDao = db2.moduleListCacheDao()
@ -533,9 +522,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// show a toast // show a toast
runOnUiThread { runOnUiThread {
Toast.makeText( Toast.makeText(
this, this, R.string.error_creating_repos_database, Toast.LENGTH_LONG
R.string.error_creating_repos_database,
Toast.LENGTH_LONG
).show() ).show()
} }
} else { } else {
@ -547,9 +534,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// show a toast // show a toast
runOnUiThread { runOnUiThread {
Toast.makeText( Toast.makeText(
this, this, R.string.error_creating_modulelistcache_database, Toast.LENGTH_LONG
R.string.error_creating_modulelistcache_database,
Toast.LENGTH_LONG
).show() ).show()
} }
} else { } else {
@ -558,7 +543,10 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// close the databases // close the databases
db.close() db.close()
db2.close() db2.close()
if (MainApplication.forceDebugLogging) Timber.d("Databases created in %s ms", System.currentTimeMillis() - startTime) if (MainApplication.forceDebugLogging) Timber.d(
"Databases created in %s ms",
System.currentTimeMillis() - startTime
)
} }
thread.start() thread.start()
} }
@ -572,17 +560,15 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// show a toast // show a toast
runOnUiThread { runOnUiThread {
Toast.makeText( Toast.makeText(
this, this, R.string.error_creating_cookie_database, Toast.LENGTH_LONG
R.string.error_creating_cookie_database,
Toast.LENGTH_LONG
).show() ).show()
} }
} }
// we literally only use these to create the http cache folders // we literally only use these to create the http cache folders
try { try {
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/cronet")) FileUtils.forceMkdir(File(MainApplication.getInstance().dataDir.toString() + "/cache/cronet"))
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm")) FileUtils.forceMkdir(File(MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm"))
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js")) FileUtils.forceMkdir(File(MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js"))
} catch (e: IOException) { } catch (e: IOException) {
Timber.e(e) Timber.e(e)
} }

@ -12,7 +12,6 @@ import android.view.View
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
@ -30,7 +29,6 @@ import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.sql.Timestamp
import java.util.Objects import java.util.Objects
class UpdateActivity : AppCompatActivity() { class UpdateActivity : AppCompatActivity() {
@ -41,27 +39,7 @@ class UpdateActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_update) setContentView(R.layout.activity_update)
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000) MainApplication.getInstance().check(this)
val buildTime = Timestamp(BuildConfig.BUILD_TIME)
if (BuildConfig.DEBUG) {
if (ts.time > buildTime.time) {
val pm = packageManager
val intent = Intent(this, ExpiredActivity::class.java)
val resolveInfo = pm.queryIntentActivities(intent, 0)
if (resolveInfo.size > 0) {
startActivity(intent)
finish()
return
} else {
throw IllegalAccessError("This build has expired")
}
}
} else {
val ts2 = Timestamp(System.currentTimeMillis() - 180L * 24 * 60 * 60 * 1000)
if (ts2.time > buildTime.time) {
Toast.makeText(this, R.string.build_expired, Toast.LENGTH_LONG).show()
}
}
chgWv = findViewById(R.id.changelog_webview) chgWv = findViewById(R.id.changelog_webview)
val changelogWebView = chgWv!! val changelogWebView = chgWv!!
val webSettings = changelogWebView.settings val webSettings = changelogWebView.settings
@ -80,18 +58,18 @@ class UpdateActivity : AppCompatActivity() {
WebView.setWebContentsDebuggingEnabled(true) WebView.setWebContentsDebuggingEnabled(true)
} }
// if app is in dark mode, force dark mode on webview // if app is in dark mode, force dark mode on webview
if (MainApplication.INSTANCE!!.isDarkTheme) { if (MainApplication.getInstance().isDarkTheme) {
// for api 33, use setAlgorithmicDarkeningAllowed, for api 29-32 use setForceDark, for api 28 and below use setForceDarkStrategy // for api 33, use setAlgorithmicDarkeningAllowed, for api 29-32 use setForceDark, for api 28 and below use setForceDarkStrategy
if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
WebSettingsCompat.setAlgorithmicDarkeningAllowed(webSettings, true) WebSettingsCompat.setAlgorithmicDarkeningAllowed(webSettings, true)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { } else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION") WebSettingsCompat.setForceDark(
WebSettingsCompat.setForceDark(webSettings, WebSettingsCompat.FORCE_DARK_ON)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
@Suppress("DEPRECATION")
WebSettingsCompat.setForceDarkStrategy(
webSettings, webSettings,
WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY WebSettingsCompat.FORCE_DARK_ON
)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
@Suppress("DEPRECATION") WebSettingsCompat.setForceDarkStrategy(
webSettings, WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY
) )
} }
} }
@ -166,9 +144,8 @@ class UpdateActivity : AppCompatActivity() {
ACTIONS.INSTALL -> { ACTIONS.INSTALL -> {
// ensure path was passed and points to a file within our cache directory. replace .. and url encoded characters // ensure path was passed and points to a file within our cache directory. replace .. and url encoded characters
val path = val path = intent.getStringExtra("path")?.trim { it <= ' ' }
intent.getStringExtra("path")?.trim { it <= ' ' } ?.replace("\\.\\.".toRegex(), "")?.replace("%2e%2e".toRegex(), "")
?.replace("\\.\\.".toRegex(), "")?.replace("%2e%2e".toRegex(), "")
if (path!!.isEmpty()) { if (path!!.isEmpty()) {
runOnUiThread { runOnUiThread {
// set status text to error // set status text to error
@ -273,7 +250,8 @@ class UpdateActivity : AppCompatActivity() {
// set button text to download // set button text to download
val button = findViewById<MaterialButton>(R.id.action_update) val button = findViewById<MaterialButton>(R.id.action_update)
button.text = getString(R.string.download_update) button.text = getString(R.string.download_update)
button.icon = AppCompatResources.getDrawable(this, R.drawable.baseline_cloud_download_24) button.icon =
AppCompatResources.getDrawable(this, R.drawable.baseline_cloud_download_24)
button.isEnabled = true button.isEnabled = true
button.visibility = View.VISIBLE button.visibility = View.VISIBLE
button.setOnClickListener { button.setOnClickListener {
@ -341,8 +319,7 @@ class UpdateActivity : AppCompatActivity() {
runOnUiThread { runOnUiThread {
// update progress bar // update progress bar
progressIndicator.setProgressCompat( progressIndicator.setProgressCompat(
(downloaded.toFloat() / total.toFloat() * 100).toInt(), (downloaded.toFloat() / total.toFloat() * 100).toInt(), true
true
) )
// update status text // update status text
statusTextView.text = getString( statusTextView.text = getString(
@ -422,9 +399,7 @@ class UpdateActivity : AppCompatActivity() {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
val context = applicationContext val context = applicationContext
val uri = FileProvider.getUriForFile( val uri = FileProvider.getUriForFile(
context, context, "${context.packageName}.file-provider", updateFile!!
"${context.packageName}.file-provider",
updateFile!!
) )
intent.setDataAndTypeAndNormalize(uri, "application/vnd.android.package-archive") intent.setDataAndTypeAndNormalize(uri, "application/vnd.android.package-archive")
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION

@ -67,10 +67,7 @@ class AndroidacyActivity : AppCompatActivity() {
var downloadMode = false var downloadMode = false
@SuppressLint( @SuppressLint(
"SetJavaScriptEnabled", "SetJavaScriptEnabled", "JavascriptInterface", "RestrictedApi", "ClickableViewAccessibility"
"JavascriptInterface",
"RestrictedApi",
"ClickableViewAccessibility"
) )
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
moduleFile = File(this.cacheDir, "module.zip") moduleFile = File(this.cacheDir, "module.zip")
@ -78,8 +75,9 @@ class AndroidacyActivity : AppCompatActivity() {
val intent = this.intent val intent = this.intent
var uri: Uri? = intent.data var uri: Uri? = intent.data
@Suppress("KotlinConstantConditions") @Suppress("KotlinConstantConditions") if (!MainApplication.checkSecret(intent) || intent.data.also {
if (!MainApplication.checkSecret(intent) || intent.data.also { uri = it!! } == null) { uri = it!!
} == null) {
Timber.w("Impersonation detected") Timber.w("Impersonation detected")
finish() finish()
return return
@ -153,26 +151,24 @@ class AndroidacyActivity : AppCompatActivity() {
webSettings?.domStorageEnabled = true webSettings?.domStorageEnabled = true
webSettings?.javaScriptEnabled = true webSettings?.javaScriptEnabled = true
webSettings?.cacheMode = WebSettings.LOAD_DEFAULT webSettings?.cacheMode = WebSettings.LOAD_DEFAULT
webSettings?.allowFileAccess = false
webSettings?.allowContentAccess = false
webSettings?.mediaPlaybackRequiresUserGesture = false webSettings?.mediaPlaybackRequiresUserGesture = false
// enable webview debugging on debug builds // enable webview debugging on debug builds
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
WebView.setWebContentsDebuggingEnabled(true) WebView.setWebContentsDebuggingEnabled(true)
} }
// if app is in dark mode, force dark mode on webview // if app is in dark mode, force dark mode on webview
if (MainApplication.INSTANCE!!.isDarkTheme) { if (MainApplication.getInstance().isDarkTheme) {
// for api 33, use setAlgorithmicDarkeningAllowed, for api 29-32 use setForceDark, for api 28 and below use setForceDarkStrategy // for api 33, use setAlgorithmicDarkeningAllowed, for api 29-32 use setForceDark, for api 28 and below use setForceDarkStrategy
if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
WebSettingsCompat.setAlgorithmicDarkeningAllowed(webSettings!!, true) WebSettingsCompat.setAlgorithmicDarkeningAllowed(webSettings!!, true)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { } else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION") WebSettingsCompat.setForceDark(
WebSettingsCompat.setForceDark(webSettings!!, WebSettingsCompat.FORCE_DARK_ON)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
@Suppress("DEPRECATION")
WebSettingsCompat.setForceDarkStrategy(
webSettings!!, webSettings!!,
WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY WebSettingsCompat.FORCE_DARK_ON
)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
@Suppress("DEPRECATION") WebSettingsCompat.setForceDarkStrategy(
webSettings!!, WebSettingsCompat.DARK_STRATEGY_WEB_THEME_DARKENING_ONLY
) )
} }
} }
@ -187,8 +183,7 @@ class AndroidacyActivity : AppCompatActivity() {
wbv?.webViewClient = object : WebViewClientCompat() { wbv?.webViewClient = object : WebViewClientCompat() {
private var pageUrl: String? = null private var pageUrl: String? = null
override fun shouldOverrideUrlLoading( override fun shouldOverrideUrlLoading(
view: WebView, view: WebView, request: WebResourceRequest
request: WebResourceRequest
): Boolean { ): Boolean {
// Don't open non Androidacy urls inside WebView // Don't open non Androidacy urls inside WebView
if (request.isForMainFrame && !AndroidacyUtil.isAndroidacyLink(request.url)) { if (request.isForMainFrame && !AndroidacyUtil.isAndroidacyLink(request.url)) {
@ -204,8 +199,7 @@ class AndroidacyActivity : AppCompatActivity() {
} }
override fun shouldInterceptRequest( override fun shouldInterceptRequest(
view: WebView, view: WebView, request: WebResourceRequest
request: WebResourceRequest
): WebResourceResponse? { ): WebResourceResponse? {
return if (megaIntercept(pageUrl, request.url.toString())) { return if (megaIntercept(pageUrl, request.url.toString())) {
// Block request as Androidacy doesn't allow duplicate requests // Block request as Androidacy doesn't allow duplicate requests
@ -237,18 +231,13 @@ class AndroidacyActivity : AppCompatActivity() {
@Deprecated("Deprecated in Java") @Deprecated("Deprecated in Java")
override fun onReceivedError( override fun onReceivedError(
view: WebView, view: WebView, errorCode: Int, description: String, failingUrl: String
errorCode: Int,
description: String,
failingUrl: String
) { ) {
this.onReceivedError(failingUrl, errorCode) this.onReceivedError(failingUrl, errorCode)
} }
override fun onReceivedError( override fun onReceivedError(
view: WebView, view: WebView, request: WebResourceRequest, error: WebResourceErrorCompat
request: WebResourceRequest,
error: WebResourceErrorCompat
) { ) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE)) { if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE)) {
this.onReceivedError(request.url.toString(), error.errorCode) this.onReceivedError(request.url.toString(), error.errorCode)
@ -256,9 +245,7 @@ class AndroidacyActivity : AppCompatActivity() {
} }
override fun onReceivedSslError( override fun onReceivedSslError(
view: WebView, view: WebView, handler: SslErrorHandler, error: SslError
handler: SslErrorHandler,
error: SslError
) { ) {
super.onReceivedSslError(view, handler, error) super.onReceivedSslError(view, handler, error)
// log the error and url of its request // log the error and url of its request
@ -280,14 +267,11 @@ class AndroidacyActivity : AppCompatActivity() {
// start file chooser activity // start file chooser activity
val intent1 = fileChooserParams.createIntent() val intent1 = fileChooserParams.createIntent()
try { try {
@Suppress("DEPRECATION") @Suppress("DEPRECATION") startActivityForResult(intent1, 1)
startActivityForResult(intent1, 1)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
Toast.makeText( Toast.makeText(
this@AndroidacyActivity, this@AndroidacyActivity, R.string.file_picker_failure, Toast.LENGTH_SHORT
R.string.file_picker_failure,
Toast.LENGTH_SHORT
).show() ).show()
} }
return true return true
@ -312,24 +296,21 @@ class AndroidacyActivity : AppCompatActivity() {
if (downloadMode) return if (downloadMode) return
if (newProgress != 100 && prgInd.visibility != View.VISIBLE) { if (newProgress != 100 && prgInd.visibility != View.VISIBLE) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Progress: %d, showing progress bar", "Progress: %d, showing progress bar", newProgress
newProgress
) )
prgInd.visibility = View.VISIBLE prgInd.visibility = View.VISIBLE
} }
// if progress is greater than one, set indeterminate to false // if progress is greater than one, set indeterminate to false
if (newProgress > 1) { if (newProgress > 1) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Progress: %d, setting indeterminate to false", "Progress: %d, setting indeterminate to false", newProgress
newProgress
) )
prgInd.isIndeterminate = false prgInd.isIndeterminate = false
} }
prgInd.setProgress(newProgress, true) prgInd.setProgress(newProgress, true)
if (newProgress == 100 && prgInd.visibility != View.GONE) { if (newProgress == 100 && prgInd.visibility != View.GONE) {
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Progress: %d, hiding progress bar", "Progress: %d, hiding progress bar", newProgress
newProgress
) )
prgInd.isIndeterminate = true prgInd.isIndeterminate = true
prgInd.visibility = View.GONE prgInd.visibility = View.GONE
@ -366,8 +347,7 @@ class AndroidacyActivity : AppCompatActivity() {
} }
backOnResume = true backOnResume = true
if (MainApplication.forceDebugLogging) Timber.i( if (MainApplication.forceDebugLogging) Timber.i(
"Exiting WebView %s", "Exiting WebView %s", AndroidacyUtil.hideToken(downloadUrl)
AndroidacyUtil.hideToken(downloadUrl)
) )
for (prefix in arrayOf<String>( for (prefix in arrayOf<String>(
"https://production-api.androidacy.com/magisk/file//", "https://production-api.androidacy.com/magisk/file//",
@ -387,6 +367,9 @@ class AndroidacyActivity : AppCompatActivity() {
val headers = HashMap<String, String>() val headers = HashMap<String, String>()
headers["Accept-Language"] = this.resources.configuration.locales.get(0).language headers["Accept-Language"] = this.resources.configuration.locales.get(0).language
// set layout to view // set layout to view
if (BuildConfig.DEBUG) {
Timber.i("AndroidacyActivity: loading url: %s", url)
}
wbv?.loadUrl(url, headers) wbv?.loadUrl(url, headers)
} }
@ -471,11 +454,7 @@ class AndroidacyActivity : AppCompatActivity() {
val checksum = AndroidacyUtil.getChecksumFromURL(fileUrl) val checksum = AndroidacyUtil.getChecksumFromURL(fileUrl)
val moduleTitle = AndroidacyUtil.getModuleTitle(fileUrl) val moduleTitle = AndroidacyUtil.getModuleTitle(fileUrl)
androidacyWebAPI!!.openNativeModuleDialogRaw( androidacyWebAPI!!.openNativeModuleDialogRaw(
fileUrl, fileUrl, moduleId, moduleTitle, checksum, androidacyWebAPI.canInstall()
moduleId,
moduleTitle,
checksum,
androidacyWebAPI.canInstall()
) )
return true return true
} }
@ -489,13 +468,12 @@ class AndroidacyActivity : AppCompatActivity() {
} }
var module: ByteArray? var module: ByteArray?
try { try {
module = doHttpGet( module = doHttpGet(url!!, ({ downloaded: Int, total: Int, _: Boolean ->
url!!, ({ downloaded: Int, total: Int, _: Boolean -> progressIndicator!!.setProgressCompat(
progressIndicator!!.setProgressCompat( downloaded * 100 / total, true
downloaded * 100 / total, true )
)
} as Http.ProgressListener?)!!) } as Http.ProgressListener?)!!)
FileOutputStream(moduleFile).use { fileOutputStream -> fileOutputStream.write(module) } FileOutputStream(moduleFile).use { fileOutputStream -> fileOutputStream.write(module) }
} finally { } finally {
module = null module = null
@ -503,8 +481,11 @@ class AndroidacyActivity : AppCompatActivity() {
} }
backOnResume = true backOnResume = true
downloadMode = false downloadMode = false
@Suppress("ktConcatNullable") @Suppress("ktConcatNullable") return FileProvider.getUriForFile(
return FileProvider.getUriForFile(this, this.packageName + ".file-provider", moduleFile!!) this,
this.packageName + ".file-provider",
moduleFile!!
)
} }
override fun onDestroy() { override fun onDestroy() {

@ -6,15 +6,15 @@ package com.fox2code.mmm.androidacy
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit
import androidx.core.net.toUri
import com.fingerprintjs.android.fingerprint.Fingerprinter import com.fingerprintjs.android.fingerprint.Fingerprinter
import com.fingerprintjs.android.fingerprint.FingerprinterFactory.create import com.fingerprintjs.android.fingerprint.FingerprinterFactory.create
import com.fox2code.mmm.BuildConfig import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.MainApplication.Companion.getPreferences import com.fox2code.mmm.MainApplication.Companion.getPreferences
import com.fox2code.mmm.R import com.fox2code.mmm.R
import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.hideToken import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.hideToken
@ -32,7 +32,6 @@ import com.fox2code.mmm.utils.io.net.HttpException
import com.fox2code.mmm.utils.io.net.HttpException.Companion.shouldTimeout import com.fox2code.mmm.utils.io.net.HttpException.Companion.shouldTimeout
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import okhttp3.HttpUrl.Builder import okhttp3.HttpUrl.Builder
import okhttp3.HttpUrl.Builder.*
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
@ -79,8 +78,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
val deviceId = generateDeviceId() val deviceId = generateDeviceId()
return try { return try {
val resp = doHttpGet( val resp = doHttpGet(
"https://$host/auth/me?token=$token&device_id=$deviceId&client_id=$clientID", "https://$host/auth/me?token=$token&device_id=$deviceId&client_id=$clientID", false
false
) )
// response is JSON // response is JSON
val jsonObject = JSONObject(String(resp)) val jsonObject = JSONObject(String(resp))
@ -89,24 +87,24 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
val memberPermissions = jsonObject.getJSONArray("permissions") val memberPermissions = jsonObject.getJSONArray("permissions")
// set role and permissions on userInfo property // set role and permissions on userInfo property
userInfo = arrayOf( userInfo = arrayOf(
arrayOf("role", memberLevel), arrayOf("role", memberLevel), arrayOf("permissions", memberPermissions.toString())
arrayOf("permissions", memberPermissions.toString())
) )
true true
} catch (e: HttpException) { } catch (e: HttpException) {
if (e.errorCode == 401) { if (e.errorCode == 401) {
Timber.w("Invalid token, resetting...") Timber.w("Invalid token, resetting...")
// Remove saved preference // Remove saved preference
val editor = getPreferences("androidacy")!!.edit() getPreferences("androidacy")!!.edit {
editor.remove("pref_androidacy_api_token") remove("pref_androidacy_api_token")
editor.apply() }
return false return false
} else { } else {
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
handler.post { handler.post {
Toast.makeText( Toast.makeText(
INSTANCE!!.lastActivity, MainApplication.getInstance().lastActivity,
INSTANCE!!.getString(R.string.androidacy_api_error, e.errorCode), MainApplication.getInstance()
.getString(R.string.androidacy_api_error, e.errorCode),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
@ -118,14 +116,13 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
Timber.w("Invalid token, resetting...") Timber.w("Invalid token, resetting...")
Timber.w(e) Timber.w(e)
// Remove saved preference // Remove saved preference
val editor = getPreferences("androidacy")!!.edit() getPreferences("androidacy")!!.edit {
editor.remove("pref_androidacy_api_token") remove("pref_androidacy_api_token")
editor.apply() }
requestNewToken() requestNewToken()
isValidToken( isValidToken(
getPreferences("androidacy")!!.getString( getPreferences("androidacy")!!.getString(
"pref_androidacy_api_token", "pref_androidacy_api_token", null
null
) )
) )
} }
@ -153,20 +150,19 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
} }
} }
// Save the token to the shared preferences // Save the token to the shared preferences
val editor = getPreferences("androidacy")!!.edit() getPreferences("androidacy")!!.edit {
editor.putString("pref_androidacy_api_token", token) putString("pref_androidacy_api_token", token)
editor.apply() }
return token return token
} }
@SuppressLint("RestrictedApi", "BinaryOperationInTimber") @SuppressLint("RestrictedApi", "BinaryOperationInTimber")
override fun prepare(): Boolean { override fun prepare(): Boolean {
// If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return // If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return
@Suppress("KotlinConstantConditions") @Suppress("KotlinConstantConditions") if (BuildConfig.ANDROIDACY_CLIENT_ID == "") {
if (BuildConfig.ANDROIDACY_CLIENT_ID == "") { getPreferences("mmm")!!.edit {
val editor = getPreferences("mmm")!!.edit() putBoolean("pref_androidacy_repo_enabled", false)
editor.putBoolean("pref_androidacy_repo_enabled", false) }
editor.apply()
Timber.w("ANDROIDACY_CLIENT_ID is empty, disabling AndroidacyRepoData 2") Timber.w("ANDROIDACY_CLIENT_ID is empty, disabling AndroidacyRepoData 2")
return false return false
} }
@ -182,18 +178,16 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
// If it's a 400, the app is probably outdated. Show a snackbar suggesting user update app and webview // If it's a 400, the app is probably outdated. Show a snackbar suggesting user update app and webview
if (connection.responseCode == 400) { if (connection.responseCode == 400) {
// Show a dialog using androidacy_update_needed string // Show a dialog using androidacy_update_needed string
INSTANCE?.let { MaterialAlertDialogBuilder(it) }!! MaterialAlertDialogBuilder(MainApplication.getInstance()).setTitle(R.string.androidacy_update_needed)
.setTitle(R.string.androidacy_update_needed)
.setMessage( .setMessage(
R.string.androidacy_update_needed_message R.string.androidacy_update_needed_message
) ).setPositiveButton(R.string.update) { _: DialogInterface?, _: Int ->
.setPositiveButton(R.string.update) { _: DialogInterface?, _: Int ->
// Open the app's page on the Play Store // Open the app's page on the Play Store
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
intent.data = intent.data =
Uri.parse("https://www.androidacy.com/downloads/?view=FoxMMM&utm_source=foxmnm&utm_medium=app&utm_campaign=android-app") "https://www.androidacy.com/downloads/?view=FoxMMM&utm_source=foxmnm&utm_medium=app&utm_campaign=android-app".toUri()
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
INSTANCE!!.startActivity(intent) MainApplication.getInstance().startActivity(intent)
}.setNegativeButton(R.string.cancel, null).show() }.setNegativeButton(R.string.cancel, null).show()
} }
return false return false
@ -208,8 +202,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
androidacyBlockade = time + 30000L androidacyBlockade = time + 30000L
try { try {
if (token == null) { if (token == null) {
token = token = getPreferences("androidacy")?.getString("pref_androidacy_api_token", null)
getPreferences("androidacy")?.getString("pref_androidacy_api_token", null)
if (token != null && !isValidToken(token)) { if (token != null && !isValidToken(token)) {
if (MainApplication.forceDebugLogging) Timber.i("Token expired or invalid, requesting new one...") if (MainApplication.forceDebugLogging) Timber.i("Token expired or invalid, requesting new one...")
token = null token = null
@ -243,7 +236,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
val handler = Handler(mainLooper) val handler = Handler(mainLooper)
handler.post { handler.post {
Toast.makeText( Toast.makeText(
INSTANCE!!.lastActivity, MainApplication.getInstance().lastActivity,
R.string.androidacy_failed_to_validate_token, R.string.androidacy_failed_to_validate_token,
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
@ -251,9 +244,9 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
return false return false
} else { } else {
// Save token to shared preference // Save token to shared preference
val editor = getPreferences("androidacy")!!.edit() getPreferences("androidacy")!!.edit {
editor.putString("pref_androidacy_api_token", token) putString("pref_androidacy_api_token", token)
editor.apply() }
if (MainApplication.forceDebugLogging) Timber.i("Token saved to shared preference") if (MainApplication.forceDebugLogging) Timber.i("Token saved to shared preference")
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -300,8 +293,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
jsonObject = jsonArray.getJSONObject(i) jsonObject = jsonArray.getJSONObject(i)
} else { } else {
if (MainApplication.forceDebugLogging) Timber.d( if (MainApplication.forceDebugLogging) Timber.d(
"Skipping null module at index %d", "Skipping null module at index %d", i
i
) )
continue continue
} }
@ -471,8 +463,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
companion object { companion object {
private var ANDROIDACY_DEVICE_ID: String? = null private var ANDROIDACY_DEVICE_ID: String? = null
var token = var token = getPreferences("androidacy")!!.getString("pref_androidacy_api_token", null)
getPreferences("androidacy")!!.getString("pref_androidacy_api_token", null)
init { init {
@Suppress("LocalVariableName") val OK_HTTP_URL_BUILDER: Builder = @Suppress("LocalVariableName") val OK_HTTP_URL_BUILDER: Builder =
@ -485,7 +476,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
private var realInstance: AndroidacyRepoData? = null private var realInstance: AndroidacyRepoData? = null
get() { get() {
if (field === null) { if (field === null) {
field = AndroidacyRepoData(INSTANCE!!.cacheDir, false) field = AndroidacyRepoData(MainApplication.getInstance().cacheDir, false)
} }
return field return field
} }
@ -514,20 +505,19 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
} }
// Try to get the device ID from the shared preferences // Try to get the device ID from the shared preferences
val sharedPreferences = getPreferences("androidacy") val sharedPreferences = getPreferences("androidacy")
val deviceIdPref = val deviceIdPref = sharedPreferences!!.getString("device_id_v2", null)
sharedPreferences!!.getString("device_id_v2", null)
return if (deviceIdPref != null) { return if (deviceIdPref != null) {
ANDROIDACY_DEVICE_ID = deviceIdPref ANDROIDACY_DEVICE_ID = deviceIdPref
deviceIdPref deviceIdPref
} else { } else {
val fp = create(INSTANCE!!.applicationContext) val fp = create(MainApplication.getInstance().applicationContext)
fp.getFingerprint(Fingerprinter.Version.V_5) { fingerprint: String? -> fp.getFingerprint(Fingerprinter.Version.V_5) { fingerprint: String? ->
ANDROIDACY_DEVICE_ID = fingerprint ANDROIDACY_DEVICE_ID = fingerprint
// use fingerprint // use fingerprint
// Save the device ID to the shared preferences // Save the device ID to the shared preferences
val editor = sharedPreferences.edit() sharedPreferences.edit {
editor.putString("device_id_v2", ANDROIDACY_DEVICE_ID) putString("device_id_v2", ANDROIDACY_DEVICE_ID)
editor.apply() }
} }
// wait for up to 5 seconds for the fingerprint to be generated (ANDROIDACY_DEVICE_ID to be set) // wait for up to 5 seconds for the fingerprint to be generated (ANDROIDACY_DEVICE_ID to be set)
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()

@ -236,7 +236,7 @@ class AndroidacyWebAPI(
*/ */
@get:JavascriptInterface @get:JavascriptInterface
val isLightTheme: Boolean val isLightTheme: Boolean
get() = MainApplication.INSTANCE!!.isLightTheme get() = MainApplication.getInstance().isLightTheme
/** /**
* Check if the manager has received root access * Check if the manager has received root access

@ -103,7 +103,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
builder.setContentTitle(context.getString(R.string.notification_channel_background_update_app)) builder.setContentTitle(context.getString(R.string.notification_channel_background_update_app))
builder.setContentText(context.getString(R.string.notification_channel_background_update_app_description)) builder.setContentText(context.getString(R.string.notification_channel_background_update_app_description))
if (ContextCompat.checkSelfPermission( if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED ) == PackageManager.PERMISSION_GRANTED
) { ) {
@ -120,7 +120,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
) { ) {
return return
} }
if (MainApplication.INSTANCE!!.isInForeground) { if (MainApplication.getInstance().isInForeground) {
// don't check if app is in foreground, this is a background check // don't check if app is in foreground, this is a background check
return return
} }
@ -144,7 +144,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
// post checking notification if notifications are enabled // post checking notification if notifications are enabled
if (ContextCompat.checkSelfPermission( if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED ) == PackageManager.PERMISSION_GRANTED
) { ) {
@ -309,7 +309,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
} }
// remove checking notification // remove checking notification
if (ContextCompat.checkSelfPermission( if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED ) == PackageManager.PERMISSION_GRANTED
) { ) {
@ -328,7 +328,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
Timber.e(e, "Failed to check for updates") Timber.e(e, "Failed to check for updates")
// post notification // post notification
if (ContextCompat.checkSelfPermission( if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED ) == PackageManager.PERMISSION_GRANTED
) { ) {
@ -417,14 +417,14 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
// set long text to summary so it doesn't get cut off // set long text to summary so it doesn't get cut off
builder.setStyle(NotificationCompat.BigTextStyle().bigText(summary)) builder.setStyle(NotificationCompat.BigTextStyle().bigText(summary))
if (ContextCompat.checkSelfPermission( if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED ) != PackageManager.PERMISSION_GRANTED
) { ) {
return return
} }
// check if app is in foreground. if so, don't show notification // check if app is in foreground. if so, don't show notification
if (MainApplication.INSTANCE!!.isInForeground && !test) return if (MainApplication.getInstance().isInForeground && !test) return
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build()) NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build())
} }

@ -65,13 +65,17 @@ import java.io.InputStreamReader
import java.util.Enumeration import java.util.Enumeration
import java.util.concurrent.Executor import java.util.concurrent.Executor
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.isVisible
class InstallerActivity : AppCompatActivity() { class InstallerActivity : AppCompatActivity() {
private var canGoBack: Boolean = false private var canGoBack: Boolean = false
private val isLightTheme: Boolean private val isLightTheme: Boolean
get() = MainApplication.INSTANCE!!.isLightTheme get() = MainApplication.getInstance().isLightTheme
private var progressIndicator: LinearProgressIndicator? = null private var progressIndicator: LinearProgressIndicator? = null
@SuppressLint("RestrictedApi")
private var rebootFloatingButton: BottomNavigationItemView? = null private var rebootFloatingButton: BottomNavigationItemView? = null
@SuppressLint("RestrictedApi")
private var cancelFloatingButton: BottomNavigationItemView? = null private var cancelFloatingButton: BottomNavigationItemView? = null
private var installerTerminal: InstallerTerminal? = null private var installerTerminal: InstallerTerminal? = null
private var moduleCache: File? = null private var moduleCache: File? = null
@ -106,7 +110,7 @@ class InstallerActivity : AppCompatActivity() {
target = intent.getStringExtra(Constants.EXTRA_INSTALL_PATH)!!.replace( target = intent.getStringExtra(Constants.EXTRA_INSTALL_PATH)!!.replace(
Regex("(\\.\\.|%2E%2E|%252E%252E|%20)"), "" Regex("(\\.\\.|%2E%2E|%252E%252E|%20)"), ""
) )
if (target.isEmpty() || !target.startsWith(MainApplication.INSTANCE!!.dataDir.absolutePath) && !target.startsWith( if (target.isEmpty() || !target.startsWith(MainApplication.getInstance().dataDir.absolutePath) && !target.startsWith(
"https://" "https://"
) )
) { ) {
@ -140,7 +144,7 @@ class InstallerActivity : AppCompatActivity() {
setContentView(if (textWrap) R.layout.installer_wrap else R.layout.installer) setContentView(if (textWrap) R.layout.installer_wrap else R.layout.installer)
val background: Int val background: Int
val foreground: Int val foreground: Int
if (MainApplication.INSTANCE!!.isLightTheme && !MainApplication.isForceDarkTerminal) { if (MainApplication.getInstance().isLightTheme && !MainApplication.isForceDarkTerminal) {
background = Color.WHITE background = Color.WHITE
foreground = Color.BLACK foreground = Color.BLACK
} else { } else {
@ -161,7 +165,7 @@ class InstallerActivity : AppCompatActivity() {
InstallerTerminal(findViewById<RecyclerView>(R.id.install_terminal).also { InstallerTerminal(findViewById<RecyclerView>(R.id.install_terminal).also {
installTerminal = it installTerminal = it
}, this.isLightTheme, foreground, mmtReborn) }, this.isLightTheme, foreground, mmtReborn)
(horizontalScroller ?: installTerminal).background = ColorDrawable(background) (horizontalScroller ?: installTerminal).background = background.toDrawable()
installTerminal.itemAnimator = null installTerminal.itemAnimator = null
val prgInd = progressIndicator val prgInd = progressIndicator
prgInd?.visibility = View.GONE prgInd?.visibility = View.GONE
@ -188,7 +192,7 @@ class InstallerActivity : AppCompatActivity() {
} }
Thread(Runnable { Thread(Runnable {
// ensure module cache is is in our cache dir // ensure module cache is is in our cache dir
if (urlMode && !moduleCache!!.absolutePath.startsWith(MainApplication.INSTANCE!!.cacheDir.absolutePath)) throw SecurityException( if (urlMode && !moduleCache!!.absolutePath.startsWith(MainApplication.getInstance().cacheDir.absolutePath)) throw SecurityException(
"Module cache is not in cache dir!" "Module cache is not in cache dir!"
) )
toDelete = if (urlMode) File(moduleCache, "module.zip") else File( toDelete = if (urlMode) File(moduleCache, "module.zip") else File(
@ -246,7 +250,7 @@ class InstallerActivity : AppCompatActivity() {
val zipFileTemp = File(this.cacheDir, "module.zip") val zipFileTemp = File(this.cacheDir, "module.zip")
FileOutputStream(zipFileTemp).use { fos -> fos.write(rawModule) } FileOutputStream(zipFileTemp).use { fos -> fos.write(rawModule) }
try { try {
ZipFile(zipFileTemp).use { zipFile -> ZipFile.Builder().setFile(zipFileTemp).get().use { zipFile ->
// get the zip entries // get the zip entries
val zipEntries: Enumeration<out ZipEntry> = zipFile.entries val zipEntries: Enumeration<out ZipEntry> = zipFile.entries
// iterate over the zip entries // iterate over the zip entries
@ -301,7 +305,7 @@ class InstallerActivity : AppCompatActivity() {
errMessage = "Failed to patch module zip" errMessage = "Failed to patch module zip"
runOnUiThread { installerTerminal!!.addLine("- Patching $name") } runOnUiThread { installerTerminal!!.addLine("- Patching $name") }
FileOutputStream(moduleCache).use { outputStream -> FileOutputStream(moduleCache).use { outputStream ->
patchModuleSimple(rawModule!!, outputStream) patchModuleSimple(rawModule, outputStream)
outputStream.flush() outputStream.flush()
} }
} }
@ -353,7 +357,7 @@ class InstallerActivity : AppCompatActivity() {
} }
installerTerminal!!.enableAnsi() installerTerminal!!.enableAnsi()
try { try {
ZipFile(file).use { zipFile -> ZipFile.Builder().setFile(file).get().use { zipFile ->
val zipEntry = zipFile.getEntry("customize.sh") val zipEntry = zipFile.getEntry("customize.sh")
if (zipEntry != null) { if (zipEntry != null) {
FileOutputStream( FileOutputStream(
@ -372,7 +376,7 @@ class InstallerActivity : AppCompatActivity() {
} }
installerMonitor = InstallerMonitor(installScript) installerMonitor = InstallerMonitor(installScript)
installJob = Shell.cmd( installJob = Shell.cmd(
"export ASH_STANDALONE=1 exec " + ashExec, "export ASH_STANDALONE=1 exec $ashExec",
"export MMM_EXT_SUPPORT=1", "export MMM_EXT_SUPPORT=1",
"export MMM_USER_LANGUAGE=" + this.resources.configuration.locales[0].toLanguageTag(), "export MMM_USER_LANGUAGE=" + this.resources.configuration.locales[0].toLanguageTag(),
"export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME,
@ -394,7 +398,7 @@ class InstallerActivity : AppCompatActivity() {
return return
} }
try { try {
ZipFile(file).use { zipFile -> ZipFile.Builder().setFile(file).get().use { zipFile ->
// Check if module is AnyKernel module // Check if module is AnyKernel module
if (zipFile.getEntry("tools/ak3-core.sh") != null) { if (zipFile.getEntry("tools/ak3-core.sh") != null) {
val updateBinary = val updateBinary =
@ -443,9 +447,9 @@ class InstallerActivity : AppCompatActivity() {
val compatFlags = AppUpdateManager.getFlagsForModule(moduleId!!) val compatFlags = AppUpdateManager.getFlagsForModule(moduleId!!)
if (compatFlags and AppUpdateManager.FLAG_COMPAT_NEED_32BIT != 0) needs32bit = true if (compatFlags and AppUpdateManager.FLAG_COMPAT_NEED_32BIT != 0) needs32bit = true
if (compatFlags and AppUpdateManager.FLAG_COMPAT_NO_EXT != 0) noExtensions = true if (compatFlags and AppUpdateManager.FLAG_COMPAT_NO_EXT != 0) noExtensions = true
if (moduleId != null && (moduleId!!.isEmpty() || moduleId!!.contains("/") || moduleId!!.contains( if (moduleId != null && (moduleId.isEmpty() || moduleId.contains("/") || moduleId.contains(
"\u0000" "\u0000"
) || moduleId!!.startsWith(".") && moduleId!!.endsWith(".")) ) || moduleId.startsWith(".") && moduleId.endsWith("."))
) { ) {
setInstallStateFinished(false, "! This module contain a dangerous moduleId", null) setInstallStateFinished(false, "! This module contain a dangerous moduleId", null)
return return
@ -767,7 +771,7 @@ class InstallerActivity : AppCompatActivity() {
} catch (ignored: Exception) { } catch (ignored: Exception) {
progressIndicator!!.setProgressCompat(0, true) progressIndicator!!.setProgressCompat(0, true)
progressIndicator.max = 100 progressIndicator.max = 100
if (progressIndicator.visibility == View.VISIBLE) { if (progressIndicator.isVisible) {
progressIndicator.visibility = View.GONE progressIndicator.visibility = View.GONE
} }
progressIndicator.isIndeterminate = true progressIndicator.isIndeterminate = true
@ -775,12 +779,12 @@ class InstallerActivity : AppCompatActivity() {
} else { } else {
progressIndicator!!.setProgressCompat(0, true) progressIndicator!!.setProgressCompat(0, true)
progressIndicator.max = 100 progressIndicator.max = 100
if (progressIndicator.visibility == View.VISIBLE) { if (progressIndicator.isVisible) {
progressIndicator.visibility = View.GONE progressIndicator.visibility = View.GONE
} }
progressIndicator.isIndeterminate = true progressIndicator.isIndeterminate = true
} }
progressIndicator!!.visibility = View.VISIBLE progressIndicator.visibility = View.VISIBLE
} }
"setLoading" -> { "setLoading" -> {

@ -26,7 +26,7 @@ class LocalModuleInfo(id: String?) : ModuleInfo(id!!) {
get() { get() {
if (field == null) { if (field == null) {
try { try {
field = MainApplication.INSTANCE!!.repoModules[id] field = MainApplication.getInstance().repoModules[id]
} catch (e: IOException) { } catch (e: IOException) {
// ignore // ignore
} }

@ -60,7 +60,7 @@ class ModuleManager private constructor() : SyncManager() {
if (modules != null) { if (modules != null) {
if (MainApplication.forceDebugLogging) Timber.i("Found %d modules on device in data", modules.size) if (MainApplication.forceDebugLogging) Timber.i("Found %d modules on device in data", modules.size)
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
MainApplication.INSTANCE!!, MainApplication.getInstance(),
ModuleListCacheDatabase::class.java, ModuleListCacheDatabase::class.java,
"ModuleListCache.db" "ModuleListCache.db"
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
@ -213,10 +213,10 @@ class ModuleManager private constructor() : SyncManager() {
return moduleInfos return moduleInfos
} }
set(value) { set(value) {
// add to MainApplication.INSTANCE!!.localModules hashmap // add to MainApplication.getInstance().localModules hashmap
field = value field = value
moduleInfos = value moduleInfos = value
MainApplication.INSTANCE!!.localModules = value MainApplication.getInstance().localModules = value
} }
fun getUpdatableModuleCount(): Int { fun getUpdatableModuleCount(): Int {

@ -95,7 +95,7 @@ class MarkdownActivity : AppCompatActivity() {
val markdown = String(rawMarkdown, StandardCharsets.UTF_8) val markdown = String(rawMarkdown, StandardCharsets.UTF_8)
if (MainApplication.forceDebugLogging) Timber.i("Done!") if (MainApplication.forceDebugLogging) Timber.i("Done!")
runOnUiThread { runOnUiThread {
MainApplication.INSTANCE!!.markwon?.setMarkdown( MainApplication.getInstance().markwon?.setMarkdown(
textView, MarkdownUrlLinker.urlLinkify(markdown) textView, MarkdownUrlLinker.urlLinkify(markdown)
) )
if (markdownBackground != null) { if (markdownBackground != null) {

@ -5,14 +5,13 @@ package com.fox2code.mmm.module
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface import android.content.DialogInterface
import android.net.Uri
import android.text.Html import android.text.Html
import android.text.Spanned import android.text.Spanned
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.net.toUri
import com.fox2code.mmm.MainApplication import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.MainApplication.Companion.isShowcaseMode import com.fox2code.mmm.MainApplication.Companion.isShowcaseMode
import com.fox2code.mmm.R import com.fox2code.mmm.R
import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.isAndroidacyLink import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.isAndroidacyLink
@ -69,9 +68,7 @@ enum class ActionButtonType {
Timber.e(e) Timber.e(e)
Timber.e("Error opening notes - androidacy link. This should never happen.") Timber.e("Error opening notes - androidacy link. This should never happen.")
Toast.makeText( Toast.makeText(
button.context, button.context, R.string.error_opening_notes, Toast.LENGTH_SHORT
R.string.error_opening_notes,
Toast.LENGTH_SHORT
).show() ).show()
} }
} else { } else {
@ -90,9 +87,7 @@ enum class ActionButtonType {
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
Toast.makeText( Toast.makeText(
button.context, button.context, R.string.error_opening_notes, Toast.LENGTH_SHORT
R.string.error_opening_notes,
Toast.LENGTH_SHORT
).show() ).show()
} }
} }
@ -130,23 +125,17 @@ enum class ActionButtonType {
) { ) {
// get safe status from either mainmoduleinfo or repo module // get safe status from either mainmoduleinfo or repo module
val safe = val safe =
moduleHolder.mainModuleInfo.safe || moduleHolder.repoModule?.moduleInfo?.safe ?: false moduleHolder.mainModuleInfo.safe || moduleHolder.repoModule?.moduleInfo?.safe == true
if (!safe) { if (!safe) {
// block local install for safety // block local install for safety
MaterialAlertDialogBuilder(button.context) MaterialAlertDialogBuilder(button.context).setTitle(R.string.install_blocked)
.setTitle(R.string.install_blocked)
.setMessage(R.string.install_blocked_message) .setMessage(R.string.install_blocked_message)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null).show()
.show()
return return
} }
} }
// if mainmoduleinfo is null, we are in repo mode // if mainmoduleinfo is null, we are in repo mode
val moduleInfo: ModuleInfo = if (moduleHolder.mainModuleInfo != null) { val moduleInfo: ModuleInfo = moduleHolder.mainModuleInfo
moduleHolder.mainModuleInfo
} else {
moduleHolder.repoModule?.moduleInfo ?: return
}
val name: String? = if (moduleHolder.moduleInfo != null) { val name: String? = if (moduleHolder.moduleInfo != null) {
moduleHolder.moduleInfo!!.name moduleHolder.moduleInfo!!.name
} else { } else {
@ -163,8 +152,12 @@ enum class ActionButtonType {
updateZipUrl = moduleHolder.repoModule!!.zipUrl!! updateZipUrl = moduleHolder.repoModule!!.zipUrl!!
} }
// check if MainApplicaiton.repomodules contains the module // check if MainApplicaiton.repomodules contains the module
if (updateZipUrl.isEmpty() && INSTANCE!!.repoModules.containsKey(moduleInfo.id)) { if (updateZipUrl.isEmpty() && MainApplication.getInstance().repoModules.containsKey(
updateZipUrl = INSTANCE!!.repoModules[moduleInfo.id]?.zipUrl.toString() moduleInfo.id
)
) {
updateZipUrl =
MainApplication.getInstance().repoModules[moduleInfo.id]?.zipUrl.toString()
} }
// if repomodule is null, try localmoduleinfo // if repomodule is null, try localmoduleinfo
if (updateZipUrl.isEmpty() && moduleHolder.moduleInfo != null && moduleHolder.moduleInfo!!.updateZipUrl != null) { if (updateZipUrl.isEmpty() && moduleHolder.moduleInfo != null && moduleHolder.moduleInfo!!.updateZipUrl != null) {
@ -183,11 +176,7 @@ enum class ActionButtonType {
// Androidacy manage the selection between download and install // Androidacy manage the selection between download and install
if (isAndroidacyLink(updateZipUrl)) { if (isAndroidacyLink(updateZipUrl)) {
openUrlAndroidacy( openUrlAndroidacy(
button.context, button.context, updateZipUrl, true, moduleInfo.name, moduleInfo.config
updateZipUrl,
true,
moduleInfo.name,
moduleInfo.config
) )
return return
} }
@ -199,7 +188,7 @@ enum class ActionButtonType {
var markwon: Markwon? = null var markwon: Markwon? = null
val localModuleInfo = moduleHolder.moduleInfo val localModuleInfo = moduleHolder.moduleInfo
if (localModuleInfo != null && localModuleInfo.updateChangeLog.isNotEmpty()) { if (localModuleInfo != null && localModuleInfo.updateChangeLog.isNotEmpty()) {
markwon = INSTANCE!!.markwon markwon = MainApplication.getInstance().markwon
// Re-render each time in cse of config changes // Re-render each time in cse of config changes
desc = markwon!!.toMarkdown(localModuleInfo.updateChangeLog) desc = markwon!!.toMarkdown(localModuleInfo.updateChangeLog)
} }
@ -232,13 +221,12 @@ enum class ActionButtonType {
} }
} }
ExternalHelper.INSTANCE.injectButton( ExternalHelper.INSTANCE.injectButton(
builder, builder, { updateZipUrl.toUri() }, moduleHolder.updateZipRepo
{ Uri.parse(updateZipUrl) },
moduleHolder.updateZipRepo
)
val dim5dp = INSTANCE!!.lastActivity?.resources!!.getDimensionPixelSize(
R.dimen.dim5dp
) )
val dim5dp =
MainApplication.getInstance().lastActivity?.resources!!.getDimensionPixelSize(
R.dimen.dim5dp
)
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp) builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp)
val alertDialog = builder.show() val alertDialog = builder.show()
for (i in -3..-1) { for (i in -3..-1) {
@ -315,7 +303,7 @@ enum class ActionButtonType {
.show() .show()
} else { } else {
moduleHolder.moduleInfo = null moduleHolder.moduleInfo = null
INSTANCE!!.lastActivity!! MainApplication.getInstance().lastActivity!!
Timber.e("Cleared: %s", moduleId) Timber.e("Cleared: %s", moduleId)
} }
} }
@ -327,8 +315,7 @@ enum class ActionButtonType {
}, },
CONFIG { CONFIG {
override fun update(button: Chip, moduleHolder: ModuleHolder) { override fun update(button: Chip, moduleHolder: ModuleHolder) {
button.chipIcon = button.chipIcon = button.context.getDrawable(R.drawable.ic_baseline_app_settings_alt_24)
button.context.getDrawable(R.drawable.ic_baseline_app_settings_alt_24)
button.setText(R.string.config) button.setText(R.string.config)
} }
@ -424,15 +411,13 @@ enum class ActionButtonType {
MaterialAlertDialogBuilder(button.context).setTitle(R.string.warning) MaterialAlertDialogBuilder(button.context).setTitle(R.string.warning)
.setMessage(R.string.warning_message).setPositiveButton( .setMessage(R.string.warning_message).setPositiveButton(
R.string.understand R.string.understand
) { _: DialogInterface?, _: Int -> } ) { _: DialogInterface?, _: Int -> }.create().show()
.create().show()
} }
}, },
SAFE { SAFE {
// SAFE is for modules that the api says are clean. only supported by androidacy currently // SAFE is for modules that the api says are clean. only supported by androidacy currently
override fun update(button: Chip, moduleHolder: ModuleHolder) { override fun update(button: Chip, moduleHolder: ModuleHolder) {
button.chipIcon = button.chipIcon = button.context.getDrawable(R.drawable.baseline_verified_user_24)
button.context.getDrawable(R.drawable.baseline_verified_user_24)
button.setText(R.string.safe) button.setText(R.string.safe)
} }
@ -451,16 +436,14 @@ enum class ActionButtonType {
MaterialAlertDialogBuilder(button.context).setTitle(R.string.safe_module) MaterialAlertDialogBuilder(button.context).setTitle(R.string.safe_module)
.setMessage(R.string.safe_message).setPositiveButton( .setMessage(R.string.safe_message).setPositiveButton(
R.string.understand R.string.understand
) { _: DialogInterface?, _: Int -> } ) { _: DialogInterface?, _: Int -> }.create().show()
.create().show()
} }
}, },
REMOTE { REMOTE {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
override fun doAction(button: Chip, moduleHolder: ModuleHolder) { override fun doAction(button: Chip, moduleHolder: ModuleHolder) {
if (MainApplication.forceDebugLogging) Timber.d( if (MainApplication.forceDebugLogging) Timber.d(
"doAction: remote module for %s", "doAction: remote module for %s", moduleHolder.moduleInfo?.name ?: "null"
moduleHolder.moduleInfo?.name ?: "null"
) )
// that module is from remote repo // that module is from remote repo
val name: String? = if (moduleHolder.moduleInfo != null) { val name: String? = if (moduleHolder.moduleInfo != null) {
@ -477,11 +460,7 @@ enum class ActionButtonType {
} }
val madb = MaterialAlertDialogBuilder(button.context) val madb = MaterialAlertDialogBuilder(button.context)
madb.setTitle(R.string.remote_module) madb.setTitle(R.string.remote_module)
val moduleInfo: ModuleInfo = if (moduleHolder.mainModuleInfo != null) { val moduleInfo: ModuleInfo = moduleHolder.mainModuleInfo
moduleHolder.mainModuleInfo
} else {
moduleHolder.repoModule?.moduleInfo ?: return
}
var updateZipUrl = moduleHolder.updateZipUrl var updateZipUrl = moduleHolder.updateZipUrl
if (updateZipUrl.isNullOrEmpty()) { if (updateZipUrl.isNullOrEmpty()) {
// try repoModule.zipUrl // try repoModule.zipUrl
@ -489,7 +468,7 @@ enum class ActionButtonType {
if (repoModule?.zipUrl.isNullOrEmpty()) { if (repoModule?.zipUrl.isNullOrEmpty()) {
Timber.e("No repo update zip url for %s", moduleInfo.name) Timber.e("No repo update zip url for %s", moduleInfo.name)
} else { } else {
updateZipUrl = repoModule?.zipUrl updateZipUrl = repoModule.zipUrl
} }
// next try localModuleInfo.updateZipUrl // next try localModuleInfo.updateZipUrl
if (updateZipUrl.isNullOrEmpty()) { if (updateZipUrl.isNullOrEmpty()) {
@ -519,8 +498,7 @@ enum class ActionButtonType {
madb.setMessage( madb.setMessage(
Html.fromHtml( Html.fromHtml(
button.context.getString( button.context.getString(
R.string.remote_message, R.string.remote_message, name
name
), Html.FROM_HTML_MODE_COMPACT ), Html.FROM_HTML_MODE_COMPACT
) )
) )
@ -528,8 +506,7 @@ enum class ActionButtonType {
R.string.reinstall R.string.reinstall
) { _: DialogInterface?, _: Int -> ) { _: DialogInterface?, _: Int ->
if (MainApplication.forceDebugLogging) Timber.d( if (MainApplication.forceDebugLogging) Timber.d(
"Set moduleinfo to %s", "Set moduleinfo to %s", moduleInfo.name
moduleInfo.name
) )
val name: String? = if (moduleHolder.moduleInfo != null) { val name: String? = if (moduleHolder.moduleInfo != null) {
moduleHolder.moduleInfo!!.name moduleHolder.moduleInfo!!.name
@ -537,8 +514,7 @@ enum class ActionButtonType {
moduleHolder.repoModule?.moduleInfo?.name moduleHolder.repoModule?.moduleInfo?.name
} }
if (MainApplication.forceDebugLogging) Timber.d( if (MainApplication.forceDebugLogging) Timber.d(
"doAction: remote module for %s", "doAction: remote module for %s", name
name
) )
if (MainApplication.analyticsAllowed()) { if (MainApplication.analyticsAllowed()) {
Countly.sharedInstance().events() Countly.sharedInstance().events()
@ -550,11 +526,7 @@ enum class ActionButtonType {
if (isAndroidacyLink(updateZipUrl)) { if (isAndroidacyLink(updateZipUrl)) {
if (MainApplication.forceDebugLogging) Timber.d("Androidacy link detected") if (MainApplication.forceDebugLogging) Timber.d("Androidacy link detected")
openUrlAndroidacy( openUrlAndroidacy(
button.context, button.context, updateZipUrl, true, moduleInfo.name, moduleInfo.config
updateZipUrl,
true,
moduleInfo.name,
moduleInfo.config
) )
return@setPositiveButton return@setPositiveButton
} }
@ -566,7 +538,7 @@ enum class ActionButtonType {
var markwon: Markwon? = null var markwon: Markwon? = null
val localModuleInfo = moduleHolder.moduleInfo val localModuleInfo = moduleHolder.moduleInfo
if (localModuleInfo != null && localModuleInfo.updateChangeLog.isNotEmpty()) { if (localModuleInfo != null && localModuleInfo.updateChangeLog.isNotEmpty()) {
markwon = INSTANCE!!.markwon markwon = MainApplication.getInstance().markwon
// Re-render each time in cse of config changes // Re-render each time in cse of config changes
desc = markwon!!.toMarkdown(localModuleInfo.updateChangeLog) desc = markwon!!.toMarkdown(localModuleInfo.updateChangeLog)
} }
@ -598,13 +570,12 @@ enum class ActionButtonType {
} }
} }
ExternalHelper.INSTANCE.injectButton( ExternalHelper.INSTANCE.injectButton(
builder, builder, { updateZipUrl.toUri() }, moduleHolder.updateZipRepo
{ Uri.parse(updateZipUrl) },
moduleHolder.updateZipRepo
)
val dim5dp = INSTANCE!!.lastActivity?.resources!!.getDimensionPixelSize(
R.dimen.dim5dp
) )
val dim5dp =
MainApplication.getInstance().lastActivity?.resources!!.getDimensionPixelSize(
R.dimen.dim5dp
)
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp) builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp)
val alertDialog = builder.show() val alertDialog = builder.show()
for (i in -3..-1) { for (i in -3..-1) {

@ -8,7 +8,6 @@ import android.content.pm.PackageManager
import android.view.View import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.fox2code.mmm.MainApplication import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.MainApplication.Companion.formatTime import com.fox2code.mmm.MainApplication.Companion.formatTime
import com.fox2code.mmm.MainApplication.Companion.getPreferences import com.fox2code.mmm.MainApplication.Companion.getPreferences
import com.fox2code.mmm.MainApplication.Companion.isDisableLowQualityModuleFilter import com.fox2code.mmm.MainApplication.Companion.isDisableLowQualityModuleFilter
@ -71,7 +70,8 @@ class ModuleHolder : Comparable<ModuleHolder?> {
get() = if (repoModule != null && (moduleInfo == null || moduleInfo!!.versionCode < repoModule!!.moduleInfo.versionCode)) repoModule!!.moduleInfo else moduleInfo!! get() = if (repoModule != null && (moduleInfo == null || moduleInfo!!.versionCode < repoModule!!.moduleInfo.versionCode)) repoModule!!.moduleInfo else moduleInfo!!
var updateZipUrl: String? = null var updateZipUrl: String? = null
get() = if (moduleInfo == null || repoModule != null && moduleInfo!!.updateVersionCode < repoModule!!.moduleInfo.versionCode) repoModule!!.zipUrl else moduleInfo!!.updateZipUrl ?: field get() = if (moduleInfo == null || repoModule != null && moduleInfo!!.updateVersionCode < repoModule!!.moduleInfo.versionCode) repoModule!!.zipUrl else moduleInfo!!.updateZipUrl
?: field
val updateZipRepo: String? val updateZipRepo: String?
get() = if (moduleInfo == null || repoModule != null && moduleInfo!!.updateVersionCode < repoModule!!.moduleInfo.versionCode) repoModule!!.repoData.preferenceId else "update_json" get() = if (moduleInfo == null || repoModule != null && moduleInfo!!.updateVersionCode < repoModule!!.moduleInfo.versionCode) repoModule!!.repoData.preferenceId else "update_json"
@ -121,10 +121,8 @@ class ModuleHolder : Comparable<ModuleHolder?> {
var ignoreUpdate = false var ignoreUpdate = false
try { try {
if (getPreferences("mmm")?.getStringSet( if (getPreferences("mmm")?.getStringSet(
"pref_background_update_check_excludes", "pref_background_update_check_excludes", HashSet()
HashSet() )!!.contains(
)!!
.contains(
moduleInfo!!.id moduleInfo!!.id
) )
) ignoreUpdate = true ) ignoreUpdate = true
@ -134,8 +132,7 @@ class ModuleHolder : Comparable<ModuleHolder?> {
// we now have pref_background_update_check_excludes_version, which is a id:version stringset of versions the user may want to "skip" // we now have pref_background_update_check_excludes_version, which is a id:version stringset of versions the user may want to "skip"
// oh, and because i hate myself, i made ^ at the beginning match that version and newer, and $ at the end match that version and older // oh, and because i hate myself, i made ^ at the beginning match that version and newer, and $ at the end match that version and older
val stringSetT = getPreferences("mmm")?.getStringSet( val stringSetT = getPreferences("mmm")?.getStringSet(
"pref_background_update_check_excludes_version", "pref_background_update_check_excludes_version", HashSet()
HashSet()
) )
var version = "" var version = ""
if (MainApplication.forceDebugLogging) Timber.d(stringSetT.toString()) if (MainApplication.forceDebugLogging) Timber.d(stringSetT.toString())
@ -185,19 +182,22 @@ class ModuleHolder : Comparable<ModuleHolder?> {
} }
} }
if (ignoreUpdate) { if (ignoreUpdate) {
if (MainApplication.forceDebugLogging) Timber.d("Module %s has update, but is ignored", moduleId) if (MainApplication.forceDebugLogging) Timber.d(
"Module %s has update, but is ignored",
moduleId
)
Type.INSTALLABLE Type.INSTALLABLE
} else { } else {
if (hasUpdate()) { if (hasUpdate()) {
INSTANCE!!.modulesHaveUpdates = true MainApplication.getInstance().modulesHaveUpdates = true
if (!INSTANCE!!.updateModules.contains(moduleId)) { if (!MainApplication.getInstance().updateModules.contains(moduleId)) {
INSTANCE!!.updateModules += moduleId MainApplication.getInstance().updateModules += moduleId
INSTANCE!!.updateModuleCount++ MainApplication.getInstance().updateModuleCount++
} }
if (MainApplication.forceDebugLogging) Timber.d( if (MainApplication.forceDebugLogging) Timber.d(
"modulesHaveUpdates = %s, updateModuleCount = %s", "modulesHaveUpdates = %s, updateModuleCount = %s",
INSTANCE!!.modulesHaveUpdates, MainApplication.getInstance().modulesHaveUpdates,
INSTANCE!!.updateModuleCount MainApplication.getInstance().updateModuleCount
) )
Type.UPDATABLE Type.UPDATABLE
} else { } else {
@ -209,18 +209,21 @@ class ModuleHolder : Comparable<ModuleHolder?> {
} }
fun getCompareType(type: Type?): Type? { fun getCompareType(type: Type?): Type? {
return separator return separator ?: if (notificationType != null && notificationType.special) {
?: if (notificationType != null && notificationType.special) { Type.SPECIAL_NOTIFICATIONS
Type.SPECIAL_NOTIFICATIONS } else {
} else { type
type }
}
} }
fun shouldRemove(): Boolean { fun shouldRemove(): Boolean {
// if type is not installable or updatable and we have repoModule, we should remove // if type is not installable or updatable and we have repoModule, we should remove
if (type !== Type.INSTALLABLE && type !== Type.UPDATABLE && repoModule != null) { if (type !== Type.INSTALLABLE && type !== Type.UPDATABLE && repoModule != null) {
Timber.d("Removing %s because type is %s and repoModule is not null", moduleId, type.name) Timber.d(
"Removing %s because type is %s and repoModule is not null",
moduleId,
type.name
)
return true return true
} }
// if type is updatable but we don't have an update, remove // if type is updatable but we don't have an update, remove
@ -230,12 +233,20 @@ class ModuleHolder : Comparable<ModuleHolder?> {
} }
// if type is installed we have an update, remove // if type is installed we have an update, remove
if (type === Type.INSTALLED && repoModule != null && hasUpdate()) { if (type === Type.INSTALLED && repoModule != null && hasUpdate()) {
Timber.d("Removing %s because type is %s and has update and repoModule is not null", moduleId, type.name) Timber.d(
"Removing %s because type is %s and has update and repoModule is not null",
moduleId,
type.name
)
return true return true
} }
// if type is installed but repomodule is not null, we should remove // if type is installed but repomodule is not null, we should remove
if (type === Type.INSTALLED && repoModule != null) { if (type === Type.INSTALLED && repoModule != null) {
Timber.d("Removing %s because type is %s and repoModule is not null", moduleId, type.name) Timber.d(
"Removing %s because type is %s and repoModule is not null",
moduleId,
type.name
)
return true return true
} }
// if lowqualitymodulefilter is enabled and module is low quality, remove // if lowqualitymodulefilter is enabled and module is low quality, remove
@ -255,9 +266,7 @@ class ModuleHolder : Comparable<ModuleHolder?> {
} }
fun getButtons( fun getButtons(
context: Context?, context: Context?, buttonTypeList: MutableList<ActionButtonType?>, showcaseMode: Boolean
buttonTypeList: MutableList<ActionButtonType?>,
showcaseMode: Boolean
) { ) {
if (!isModuleHolder) return if (!isModuleHolder) return
val localModuleInfo = moduleInfo val localModuleInfo = moduleInfo
@ -281,16 +290,25 @@ class ModuleHolder : Comparable<ModuleHolder?> {
// set updatezipurl on moduleholder // set updatezipurl on moduleholder
if (localModuleInfo.updateZipUrl != null) { if (localModuleInfo.updateZipUrl != null) {
if (MainApplication.forceDebugLogging) Timber.d("localModuleInfo: %s", localModuleInfo.updateZipUrl) if (MainApplication.forceDebugLogging) Timber.d(
"localModuleInfo: %s",
localModuleInfo.updateZipUrl
)
updateZipUrl = localModuleInfo.updateZipUrl updateZipUrl = localModuleInfo.updateZipUrl
} }
if (repoModule != null) { if (repoModule != null) {
if (MainApplication.forceDebugLogging) Timber.d("repoModule: %s", repoModule!!.zipUrl) if (MainApplication.forceDebugLogging) Timber.d(
"repoModule: %s",
repoModule!!.zipUrl
)
updateZipUrl = repoModule!!.zipUrl updateZipUrl = repoModule!!.zipUrl
} }
// last ditch effort, try to get remoteModuleInfo from localModuleInfo // last ditch effort, try to get remoteModuleInfo from localModuleInfo
if (rInfo != null) { if (rInfo != null) {
if (MainApplication.forceDebugLogging) Timber.d("remoteModuleInfo: %s", rInfo.zipUrl) if (MainApplication.forceDebugLogging) Timber.d(
"remoteModuleInfo: %s",
rInfo.zipUrl
)
updateZipUrl = rInfo.zipUrl updateZipUrl = rInfo.zipUrl
moduleInfo?.updateZipUrl = rInfo.zipUrl moduleInfo?.updateZipUrl = rInfo.zipUrl
} }
@ -330,13 +348,23 @@ class ModuleHolder : Comparable<ModuleHolder?> {
Timber.w("Module %s has no moduleInfo", moduleId) Timber.w("Module %s has no moduleInfo", moduleId)
return false return false
} }
if (repoModule == null && !INSTANCE!!.repoModules.containsKey(moduleId)) { if (repoModule == null && !MainApplication.getInstance().repoModules.containsKey(moduleId)) {
if (moduleInfo!!.updateVersionCode > moduleInfo!!.versionCode) { if (moduleInfo!!.updateVersionCode > moduleInfo!!.versionCode) {
Timber.d("Module %s has update from %s to %s", moduleId, moduleInfo!!.versionCode, moduleInfo!!.updateVersionCode) Timber.d(
"Module %s has update from %s to %s",
moduleId,
moduleInfo!!.versionCode,
moduleInfo!!.updateVersionCode
)
return true return true
} }
} else if (repoModule != null && repoModule!!.moduleInfo.versionCode > moduleInfo!!.versionCode) { } else if (repoModule != null && repoModule!!.moduleInfo.versionCode > moduleInfo!!.versionCode) {
Timber.d("Module %s has update from repo from %s to %s", moduleId, moduleInfo!!.versionCode, repoModule!!.moduleInfo.versionCode) Timber.d(
"Module %s has update from repo from %s to %s",
moduleId,
moduleInfo!!.versionCode,
repoModule!!.moduleInfo.versionCode
)
return true return true
} }
Timber.d("Module %s has no update", moduleId) Timber.d("Module %s has no update", moduleId)
@ -351,8 +379,7 @@ class ModuleHolder : Comparable<ModuleHolder?> {
val otherType = other.getCompareType(otherTypeReal) val otherType = other.getCompareType(otherTypeReal)
val compare = selfType!!.compareTo(otherType!!) val compare = selfType!!.compareTo(otherType!!)
return if (compare != 0) compare else if (selfTypeReal === otherTypeReal) selfTypeReal.compare( return if (compare != 0) compare else if (selfTypeReal === otherTypeReal) selfTypeReal.compare(
this, this, other
other
) else selfTypeReal.compareTo(otherTypeReal) ) else selfTypeReal.compareTo(otherTypeReal)
} }
@ -454,9 +481,7 @@ class ModuleHolder : Comparable<ModuleHolder?> {
} }
}, },
INSTALLABLE( INSTALLABLE(
R.string.online_repo, R.string.online_repo, true, true
true,
true
) { ) {
override fun compare(o1: ModuleHolder?, o2: ModuleHolder?): Int { override fun compare(o1: ModuleHolder?, o2: ModuleHolder?): Int {
if (o1 != null && o2 != null) { if (o1 != null && o2 != null) {

@ -5,7 +5,6 @@ package com.fox2code.mmm.repo
import androidx.room.Room import androidx.room.Room
import com.fox2code.mmm.MainApplication import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.MainApplication.Companion.getPreferences import com.fox2code.mmm.MainApplication.Companion.getPreferences
import com.fox2code.mmm.utils.io.Hashes.Companion.hashSha256 import com.fox2code.mmm.utils.io.Hashes.Companion.hashSha256
import com.fox2code.mmm.utils.io.PropUtils.Companion.isNullString import com.fox2code.mmm.utils.io.PropUtils.Companion.isNullString
@ -109,7 +108,7 @@ class CustomRepoManager internal constructor(
} }
val id = "repo_" + hashSha256(repo.toByteArray(StandardCharsets.UTF_8)) val id = "repo_" + hashSha256(repo.toByteArray(StandardCharsets.UTF_8))
// now the same as above but for room database // now the same as above but for room database
val applicationContext = INSTANCE!!.applicationContext val applicationContext = MainApplication.getInstance().applicationContext
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
applicationContext, ReposListDatabase::class.java, "ReposList.db" applicationContext, ReposListDatabase::class.java, "ReposList.db"
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()

@ -9,7 +9,6 @@ import com.fox2code.mmm.AppUpdateManager.Companion.shouldForceHide
import com.fox2code.mmm.BuildConfig import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.R import com.fox2code.mmm.R
import com.fox2code.mmm.XRepo import com.fox2code.mmm.XRepo
import com.fox2code.mmm.manager.ModuleInfo import com.fox2code.mmm.manager.ModuleInfo
@ -124,7 +123,7 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
isForceHide = shouldForceHide(tempVarForPreferenceId) isForceHide = shouldForceHide(tempVarForPreferenceId)
// basically same as above but for room database // basically same as above but for room database
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
INSTANCE!!.applicationContext, ReposListDatabase::class.java, "ReposList.db" MainApplication.getInstance().applicationContext, ReposListDatabase::class.java, "ReposList.db"
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
val reposListRoom = db.reposListDao() val reposListRoom = db.reposListDao()
val reposListRoomList = reposListRoom.getById(preferenceId!!) val reposListRoomList = reposListRoom.getById(preferenceId!!)
@ -278,7 +277,7 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
field field
} else { } else {
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java, ReposListDatabase::class.java,
"ReposList.db", "ReposList.db",
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
@ -295,7 +294,7 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
field = value field = value
this.enabled = enabled && !isForceHide this.enabled = enabled && !isForceHide
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java, ReposListDatabase::class.java,
"ReposList.db", "ReposList.db",
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
@ -362,7 +361,7 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
// if repo starts with repo_, it's always enabled bc custom repos can't be disabled without being deleted. // if repo starts with repo_, it's always enabled bc custom repos can't be disabled without being deleted.
isForceHide = shouldForceHide(preferenceId!!) isForceHide = shouldForceHide(preferenceId!!)
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java, ReposListDatabase::class.java,
"ReposList.db", "ReposList.db",
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()

@ -231,7 +231,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
} }
} }
} }
MainApplication.INSTANCE!!.repoModules.putAll(modules) MainApplication.getInstance().repoModules.putAll(modules)
} }
if (MainApplication.forceDebugLogging) Timber.d("Finishing update") if (MainApplication.forceDebugLogging) Timber.d("Finishing update")
if (hasConnectivity()) { if (hasConnectivity()) {
@ -246,7 +246,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
if (!isLastUpdateSuccess || modules.isEmpty()) { if (!isLastUpdateSuccess || modules.isEmpty()) {
Timber.e("Failed to update %s", repoUpdaters[i]!!.repoData.name) Timber.e("Failed to update %s", repoUpdaters[i]!!.repoData.name)
// Show snackbar on main looper and add some bottom padding // Show snackbar on main looper and add some bottom padding
val context: Activity? = MainApplication.INSTANCE!!.lastActivity val context: Activity? = MainApplication.getInstance().lastActivity
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
if (context != null) { if (context != null) {
// Show material dialogue with the repo name. for androidacy repo, show an option to reset the api key. show a message then a list of errors // Show material dialogue with the repo name. for androidacy repo, show an option to reset the api key. show a message then a list of errors
@ -299,7 +299,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
} }
fun hasConnectivity(): Boolean { fun hasConnectivity(): Boolean {
return hasConnectivity(MainApplication.INSTANCE!!.applicationContext) return hasConnectivity(MainApplication.getInstance().applicationContext)
} }
private fun addRepoData(url: String, fallBackName: String?): RepoData { private fun addRepoData(url: String, fallBackName: String?): RepoData {
@ -366,7 +366,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
if (INSTANCE == null || !INSTANCE!!.initialized) { if (INSTANCE == null || !INSTANCE!!.initialized) {
synchronized(lock) { synchronized(lock) {
if (INSTANCE == null) { if (INSTANCE == null) {
val mainApplication = MainApplication.INSTANCE val mainApplication = MainApplication.getInstance()
if (mainApplication != null) { if (mainApplication != null) {
INSTANCE = RepoManager(mainApplication) INSTANCE = RepoManager(mainApplication)
onRepoManagerInitialized() onRepoManagerInitialized()
@ -384,7 +384,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
if (INSTANCE == null) { if (INSTANCE == null) {
synchronized(lock) { synchronized(lock) {
if (INSTANCE == null) { if (INSTANCE == null) {
val mainApplication = MainApplication.INSTANCE val mainApplication = MainApplication.getInstance()
if (mainApplication != null) { if (mainApplication != null) {
INSTANCE = RepoManager(mainApplication) INSTANCE = RepoManager(mainApplication)
onRepoManagerInitialized() onRepoManagerInitialized()

@ -70,7 +70,7 @@ class RepoModule {
moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_METADATA_INVALID and ModuleInfo.FLAG_MM_REMOTE_MODULE moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_METADATA_INVALID and ModuleInfo.FLAG_MM_REMOTE_MODULE
safe = moduleInfo.safe safe = moduleInfo.safe
// if mainapplication.repomodules has this module, set the flag for remote module // if mainapplication.repomodules has this module, set the flag for remote module
if (MainApplication.INSTANCE!!.repoModules.containsKey(id)) { if (MainApplication.getInstance().repoModules.containsKey(id)) {
moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_MM_REMOTE_MODULE moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_MM_REMOTE_MODULE
} }
} }

@ -35,16 +35,16 @@ class RepoUpdater(repoData2: RepoData) {
return 0 return 0
} }
// if MainApplication.repoModules is not empty, return it // if MainApplication.repoModules is not empty, return it
/*if (MainApplication.INSTANCE!!.repoModules.isNotEmpty()) { /*if (MainApplication.getInstance().repoModules.isNotEmpty()) {
if (MainApplication.forceDebugLogging) Timber.d("Returning MainApplication.repoModules for %s", repoData.preferenceId) if (MainApplication.forceDebugLogging) Timber.d("Returning MainApplication.repoModules for %s", repoData.preferenceId)
// convert to list for toUpdate // convert to list for toUpdate
val toUpdateList = ArrayList<RepoModule>() val toUpdateList = ArrayList<RepoModule>()
for (module in MainApplication.INSTANCE!!.repoModules) { for (module in MainApplication.getInstance().repoModules) {
toUpdateList.add(module.value) toUpdateList.add(module.value)
} }
toUpdate = toUpdateList toUpdate = toUpdateList
// toapply is a collection of RepoModule, so we need to convert the list to a set // toapply is a collection of RepoModule, so we need to convert the list to a set
toApply = HashSet(MainApplication.INSTANCE!!.repoModules.values) toApply = HashSet(MainApplication.getInstance().repoModules.values)
return toUpdate!!.size return toUpdate!!.size
}*/ }*/
// if we shouldn't update, get the values from the ModuleListCache realm // if we shouldn't update, get the values from the ModuleListCache realm
@ -52,7 +52,7 @@ class RepoUpdater(repoData2: RepoData) {
if (MainApplication.forceDebugLogging) Timber.d("Fetching index from cache for %s", repoData.preferenceId) if (MainApplication.forceDebugLogging) Timber.d("Fetching index from cache for %s", repoData.preferenceId)
// now the above but for room // now the above but for room
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
MainApplication.INSTANCE!!, MainApplication.getInstance(),
ModuleListCacheDatabase::class.java, ModuleListCacheDatabase::class.java,
"ModuleListCache.db" "ModuleListCache.db"
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()
@ -137,8 +137,8 @@ class RepoUpdater(repoData2: RepoData) {
toUpdate = repoData.populate(JSONObject(String(indexRaw!!, StandardCharsets.UTF_8))) toUpdate = repoData.populate(JSONObject(String(indexRaw!!, StandardCharsets.UTF_8)))
// Since we reuse instances this should work // Since we reuse instances this should work
toApply = HashSet(repoData.moduleHashMap.values) toApply = HashSet(repoData.moduleHashMap.values)
// add toApply to the hashmap MainApplication.INSTANCE!!.repoModules // add toApply to the hashmap MainApplication.getInstance().repoModules
MainApplication.INSTANCE!!.repoModules.putAll(repoData.moduleHashMap) MainApplication.getInstance().repoModules.putAll(repoData.moduleHashMap)
(toUpdate as MutableList<RepoModule>?)?.let { (toUpdate as MutableList<RepoModule>?)?.let {
(toApply as HashSet<RepoModule>).removeAll( (toApply as HashSet<RepoModule>).removeAll(
it.toSet() it.toSet()
@ -174,7 +174,7 @@ class RepoUpdater(repoData2: RepoData) {
if (indexRaw != null) { if (indexRaw != null) {
// set lastUpdate // set lastUpdate
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
MainApplication.INSTANCE!!.applicationContext, MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java, ReposListDatabase::class.java,
"ReposList.db" "ReposList.db"
).allowMainThreadQueries().build() ).allowMainThreadQueries().build()

@ -27,7 +27,7 @@ import timber.log.Timber
class AppearanceFragment : PreferenceFragmentCompat() { class AppearanceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = MainApplication.INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore
@ -99,7 +99,7 @@ class AppearanceFragment : PreferenceFragmentCompat() {
findPreference<Preference>("pref_enable_blur")!!.setSummary(R.string.blur_disabled_summary) findPreference<Preference>("pref_enable_blur")!!.setSummary(R.string.blur_disabled_summary)
// Refresh activity // Refresh activity
UiThreadHandler.handler.postDelayed({ UiThreadHandler.handler.postDelayed({
MainApplication.INSTANCE!!.updateTheme() MainApplication.getInstance().updateTheme()
}, 1) }, 1)
val intent = Intent(requireContext(), SettingsActivity::class.java) val intent = Intent(requireContext(), SettingsActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
@ -117,8 +117,8 @@ class AppearanceFragment : PreferenceFragmentCompat() {
findPreference<Preference>("pref_enable_blur")?.summary = "" findPreference<Preference>("pref_enable_blur")?.summary = ""
} }
UiThreadHandler.handler.postDelayed({ UiThreadHandler.handler.postDelayed({
MainApplication.INSTANCE!!.updateTheme() MainApplication.getInstance().updateTheme()
MainApplication.INSTANCE!!.lastActivity!! MainApplication.getInstance().lastActivity!!
}, 1) }, 1)
true true
} }
@ -130,7 +130,7 @@ class AppearanceFragment : PreferenceFragmentCompat() {
} }
disableMonet!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { disableMonet!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
UiThreadHandler.handler.postDelayed({ UiThreadHandler.handler.postDelayed({
MainApplication.INSTANCE!!.updateTheme() MainApplication.getInstance().updateTheme()
}, 1) }, 1)
true true
} }

@ -20,7 +20,7 @@ class CreditsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = MainApplication.INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore

@ -30,7 +30,7 @@ import java.util.Date
class DebugFragment : PreferenceFragmentCompat() { class DebugFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = MainApplication.INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore
@ -76,8 +76,8 @@ class DebugFragment : PreferenceFragmentCompat() {
FileUtils.forceMkdir(requireContext().cacheDir) FileUtils.forceMkdir(requireContext().cacheDir)
// create cache dirs for cronet and webview // create cache dirs for cronet and webview
FileUtils.forceMkdir(File(requireContext().cacheDir, "cronet")) FileUtils.forceMkdir(File(requireContext().cacheDir, "cronet"))
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm")) FileUtils.forceMkdir(File(MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm"))
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js")) FileUtils.forceMkdir(File(MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js"))
Toast.makeText( Toast.makeText(
requireContext(), R.string.cache_cleared, Toast.LENGTH_SHORT requireContext(), R.string.cache_cleared, Toast.LENGTH_SHORT
).show() ).show()
@ -111,7 +111,7 @@ class DebugFragment : PreferenceFragmentCompat() {
R.string.clear_data_dialogue_message R.string.clear_data_dialogue_message
).setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> ).setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
// Clear app data // Clear app data
MainApplication.INSTANCE!!.resetApp() MainApplication.getInstance().resetApp()
}.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> } }.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> }
.show() .show()
true true

@ -21,7 +21,7 @@ import timber.log.Timber
class InfoFragment : PreferenceFragmentCompat() { class InfoFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = MainApplication.INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore
@ -119,7 +119,7 @@ class InfoFragment : PreferenceFragmentCompat() {
Preference.OnPreferenceClickListener { _: Preference? -> Preference.OnPreferenceClickListener { _: Preference? ->
// open fox // open fox
IntentHelper.openUrl( IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!, "https://paypal.me/fox2code" MainApplication.getInstance().lastActivity!!, "https://paypal.me/fox2code"
) )
true true
} }
@ -151,7 +151,7 @@ class InfoFragment : PreferenceFragmentCompat() {
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show()
// open androidacy // open androidacy
IntentHelper.openUrl( IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!, MainApplication.getInstance().lastActivity!!,
"https://www.androidacy.com/membership-join/?utm_source=AMMM&utm_medium=app&utm_campaign=donate" "https://www.androidacy.com/membership-join/?utm_source=AMMM&utm_medium=app&utm_campaign=donate"
) )
true true

@ -24,7 +24,7 @@ class PrivacyFragment : PreferenceFragmentCompat() {
@SuppressLint("CommitPrefEdits") @SuppressLint("CommitPrefEdits")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = MainApplication.INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore

@ -75,7 +75,7 @@ class RepoFragment : PreferenceFragmentCompat() {
"magisk_alt_repo", "magisk_alt_repo",
java.lang.Boolean.parseBoolean(newValue.toString()) java.lang.Boolean.parseBoolean(newValue.toString())
) )
MainApplication.INSTANCE!!.repoModules.clear() MainApplication.getInstance().repoModules.clear()
true true
} }
// Disable toggling the pref_androidacy_repo_enabled on builds without an // Disable toggling the pref_androidacy_repo_enabled on builds without an
@ -114,7 +114,7 @@ class RepoFragment : PreferenceFragmentCompat() {
val enabled = androidacyRepoEnabled.isChecked val enabled = androidacyRepoEnabled.isChecked
// save the new state // save the new state
db.reposListDao().setEnabled("androidacy_repo", enabled) db.reposListDao().setEnabled("androidacy_repo", enabled)
MainApplication.INSTANCE!!.repoModules.clear() MainApplication.getInstance().repoModules.clear()
true true
} }
if (androidacyRepoEnabledPref) { if (androidacyRepoEnabledPref) {
@ -149,7 +149,7 @@ class RepoFragment : PreferenceFragmentCompat() {
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show()
// open androidacy // open androidacy
IntentHelper.openUrl( IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!, MainApplication.getInstance().lastActivity!!,
"https://www.androidacy.com/membership-join/?utm_source=AMMM&utm_medium=app&utm_campaign=donate" "https://www.androidacy.com/membership-join/?utm_source=AMMM&utm_medium=app&utm_campaign=donate"
) )
true true
@ -618,7 +618,7 @@ class RepoFragment : PreferenceFragmentCompat() {
findPreference<Preference>(preferenceName + "_website")!!.onPreferenceClickListener = findPreference<Preference>(preferenceName + "_website")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl( IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!, MainApplication.getInstance().lastActivity!!,
repoData.website repoData.website
) )
true true
@ -631,7 +631,7 @@ class RepoFragment : PreferenceFragmentCompat() {
findPreference<Preference>(preferenceName + "_support")!!.onPreferenceClickListener = findPreference<Preference>(preferenceName + "_support")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl( IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!, MainApplication.getInstance().lastActivity!!,
repoData.support repoData.support
) )
true true
@ -644,7 +644,7 @@ class RepoFragment : PreferenceFragmentCompat() {
findPreference<Preference>(preferenceName + "_submit")!!.onPreferenceClickListener = findPreference<Preference>(preferenceName + "_submit")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl( IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!, MainApplication.getInstance().lastActivity!!,
repoData.submitModule repoData.submitModule
) )
true true
@ -657,7 +657,7 @@ class RepoFragment : PreferenceFragmentCompat() {
findPreference<Preference>(preferenceName + "_donate")!!.onPreferenceClickListener = findPreference<Preference>(preferenceName + "_donate")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl( IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!, MainApplication.getInstance().lastActivity!!,
repoData.donate repoData.donate
) )
true true
@ -692,7 +692,7 @@ class RepoFragment : PreferenceFragmentCompat() {
R.string.repo_enabled_changed, R.string.repo_enabled_changed,
BaseTransientBottomBar.LENGTH_LONG BaseTransientBottomBar.LENGTH_LONG
).show() ).show()
MainApplication.INSTANCE!!.repoModules.clear() MainApplication.getInstance().repoModules.clear()
true true
} }
} }
@ -704,7 +704,7 @@ class RepoFragment : PreferenceFragmentCompat() {
preference.isVisible = true preference.isVisible = true
preference.onPreferenceClickListener = preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, homepage) IntentHelper.openUrl(MainApplication.getInstance().lastActivity!!, homepage)
true true
} }
(preference as LongClickablePreference).onPreferenceLongClickListener = (preference as LongClickablePreference).onPreferenceLongClickListener =
@ -726,7 +726,7 @@ class RepoFragment : PreferenceFragmentCompat() {
preference.setIcon(ActionButtonType.supportIconForUrl(supportUrl)) preference.setIcon(ActionButtonType.supportIconForUrl(supportUrl))
preference.onPreferenceClickListener = preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, supportUrl) IntentHelper.openUrl(MainApplication.getInstance().lastActivity!!, supportUrl)
true true
} }
(preference as LongClickablePreference).onPreferenceLongClickListener = (preference as LongClickablePreference).onPreferenceLongClickListener =
@ -748,7 +748,7 @@ class RepoFragment : PreferenceFragmentCompat() {
preference.setIcon(ActionButtonType.donateIconForUrl(donateUrl)) preference.setIcon(ActionButtonType.donateIconForUrl(donateUrl))
preference.onPreferenceClickListener = preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, donateUrl) IntentHelper.openUrl(MainApplication.getInstance().lastActivity!!, donateUrl)
true true
} }
(preference as LongClickablePreference).onPreferenceLongClickListener = (preference as LongClickablePreference).onPreferenceLongClickListener =
@ -769,7 +769,7 @@ class RepoFragment : PreferenceFragmentCompat() {
preference.isVisible = true preference.isVisible = true
preference.onPreferenceClickListener = preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, submissionUrl) IntentHelper.openUrl(MainApplication.getInstance().lastActivity!!, submissionUrl)
true true
} }
(preference as LongClickablePreference).onPreferenceLongClickListener = (preference as LongClickablePreference).onPreferenceLongClickListener =

@ -24,7 +24,7 @@ import kotlin.system.exitProcess
class SecurityFragment : PreferenceFragmentCompat() { class SecurityFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = MainApplication.INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore

@ -1,10 +1,10 @@
/* /*
* 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.
*/ */
@file:Suppress("DEPRECATION")
package com.fox2code.mmm.settings package com.fox2code.mmm.settings
import android.net.Uri
import com.fox2code.mmm.utils.IntentHelper
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActivityManager import android.app.ActivityManager
import android.content.ClipboardManager import android.content.ClipboardManager
@ -17,6 +17,8 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
@ -24,10 +26,8 @@ import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey import androidx.security.crypto.MasterKey
import com.fox2code.mmm.BuildConfig import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.CrashHandler import com.fox2code.mmm.CrashHandler
import com.fox2code.mmm.ExpiredActivity
import com.fox2code.mmm.MainActivity import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.R import com.fox2code.mmm.R
import com.fox2code.mmm.background.BackgroundUpdateChecker.Companion.onMainActivityResume import com.fox2code.mmm.background.BackgroundUpdateChecker.Companion.onMainActivityResume
import com.fox2code.mmm.utils.IntentHelper.Companion.openUrl import com.fox2code.mmm.utils.IntentHelper.Companion.openUrl
@ -37,7 +37,6 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import timber.log.Timber import timber.log.Timber
import java.sql.Timestamp
@Suppress("SENSELESS_COMPARISON") @Suppress("SENSELESS_COMPARISON")
class SettingsActivity : AppCompatActivity(), LanguageActivity, class SettingsActivity : AppCompatActivity(), LanguageActivity,
@ -103,27 +102,7 @@ class SettingsActivity : AppCompatActivity(), LanguageActivity,
setContentView(R.layout.settings_activity) setContentView(R.layout.settings_activity)
setTitle(R.string.app_name_v2) setTitle(R.string.app_name_v2)
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000) MainApplication.getInstance().check(this)
val buildTime = Timestamp(BuildConfig.BUILD_TIME)
if (BuildConfig.DEBUG) {
if (ts.time > buildTime.time) {
val pm = packageManager
val intent = Intent(this, ExpiredActivity::class.java)
val resolveInfo = pm.queryIntentActivities(intent, 0)
if (resolveInfo.size > 0) {
startActivity(intent)
finish()
return
} else {
throw IllegalAccessError("This build has expired")
}
}
} else {
val ts2 = Timestamp(System.currentTimeMillis() - 180L * 24 * 60 * 60 * 1000)
if (ts2.time > buildTime.time) {
Toast.makeText(this, R.string.build_expired, Toast.LENGTH_LONG).show()
}
}
//hideActionBar(); //hideActionBar();
bottomNavigationView = findViewById(R.id.bottom_navigation) bottomNavigationView = findViewById(R.id.bottom_navigation)
bottomNavigationView.setOnItemSelectedListener(onItemSelectedListener) bottomNavigationView.setOnItemSelectedListener(onItemSelectedListener)
@ -150,7 +129,7 @@ class SettingsActivity : AppCompatActivity(), LanguageActivity,
@SuppressLint("UnspecifiedImmutableFlag") @SuppressLint("UnspecifiedImmutableFlag")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore
@ -206,24 +185,26 @@ class SettingsActivity : AppCompatActivity(), LanguageActivity,
findPreference<Preference>("pref_pkg_info")!!.onPreferenceClickListener = findPreference<Preference>("pref_pkg_info")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { p: Preference -> Preference.OnPreferenceClickListener { p: Preference ->
versionClicks++ versionClicks++
if (MainApplication.forceDebugLogging) Timber.d("Version clicks: %d", versionClicks) if (MainApplication.forceDebugLogging) Timber.d(
"Version clicks: %d", versionClicks
)
if (versionClicks == 7) { if (versionClicks == 7) {
versionClicks = 0 versionClicks = 0
openUrl(p.context, "https://www.youtube.com/watch?v=dQw4w9WgXcQ") openUrl(p.context, "https://www.youtube.com/watch?v=dQw4w9WgXcQ")
} }
// enable dev mode if it's disabled otherwise disable it // enable dev mode if it's disabled otherwise disable it
val editor = dataStore.sharedPreferences.edit() dataStore.sharedPreferences.edit {
if (dataStore.sharedPreferences.getBoolean("developer", false)) { if (dataStore.sharedPreferences.getBoolean("developer", false)) {
editor.putBoolean("developer", false) putBoolean("developer", false)
Toast.makeText( Toast.makeText(
p.context, R.string.dev_mode_disabled, Toast.LENGTH_SHORT p.context, R.string.dev_mode_disabled, Toast.LENGTH_SHORT
).show() ).show()
} else { } else {
Toast.makeText( Toast.makeText(
p.context, R.string.dev_mode_enabled, Toast.LENGTH_SHORT p.context, R.string.dev_mode_enabled, Toast.LENGTH_SHORT
).show() ).show()
}
} }
editor.apply()
// toast yer a wizard harry // toast yer a wizard harry
if (versionClicks == 3) { if (versionClicks == 3) {
Toast.makeText( Toast.makeText(
@ -244,11 +225,11 @@ class SettingsActivity : AppCompatActivity(), LanguageActivity,
} }
findPreference<Preference>("pref_show_apps")!!.onPreferenceClickListener = findPreference<Preference>("pref_show_apps")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener { _: Preference? -> Preference.OnPreferenceClickListener { _: Preference? ->
val browserIntent = Intent( val browserIntent = Intent(
Intent.ACTION_VIEW, Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/dev?id=6763514284252789381") "https://play.google.com/store/apps/dev?id=6763514284252789381".toUri()
) )
startActivity(browserIntent) startActivity(browserIntent)
return@OnPreferenceClickListener true return@OnPreferenceClickListener true
} }
} }
@ -270,7 +251,7 @@ class SettingsActivity : AppCompatActivity(), LanguageActivity,
// device is awarded 1 point for each core and 1 point for each GB of ram. // device is awarded 1 point for each core and 1 point for each GB of ram.
var points = 0 var points = 0
val cores = Runtime.getRuntime().availableProcessors() val cores = Runtime.getRuntime().availableProcessors()
val activityManager = INSTANCE!!.getSystemService( val activityManager = MainApplication.getInstance().getSystemService(
ACTIVITY_SERVICE ACTIVITY_SERVICE
) as ActivityManager ) as ActivityManager
if (activityManager != null) { if (activityManager != null) {
@ -283,7 +264,9 @@ class SettingsActivity : AppCompatActivity(), LanguageActivity,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
points += 1 points += 1
} }
if (MainApplication.forceDebugLogging) Timber.d("Device performance class: %d", points) if (MainApplication.forceDebugLogging) Timber.d(
"Device performance class: %d", points
)
return if (points <= 7) { return if (points <= 7) {
PERFORMANCE_CLASS_LOW PERFORMANCE_CLASS_LOW
} else if (points <= 12) { } else if (points <= 12) {

@ -32,12 +32,13 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textview.MaterialTextView import com.google.android.material.textview.MaterialTextView
import timber.log.Timber import timber.log.Timber
import java.util.Random import java.util.Random
import androidx.core.content.edit
class UpdateFragment : PreferenceFragmentCompat() { class UpdateFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val name = "mmmx" val name = "mmmx"
val context: Context? = MainApplication.INSTANCE val context: Context? = MainApplication.getInstance()
val masterKey: MasterKey val masterKey: MasterKey
val preferenceManager = preferenceManager val preferenceManager = preferenceManager
val dataStore: SharedPreferenceDataStore val dataStore: SharedPreferenceDataStore
@ -69,7 +70,7 @@ class UpdateFragment : PreferenceFragmentCompat() {
findPreference<Preference>("pref_background_update_check_excludes_version") findPreference<Preference>("pref_background_update_check_excludes_version")
debugNotification!!.isEnabled = MainApplication.isBackgroundUpdateCheckEnabled debugNotification!!.isEnabled = MainApplication.isBackgroundUpdateCheckEnabled
debugNotification.isVisible = debugNotification.isVisible =
MainApplication.isDeveloper && !MainApplication.isWrapped && MainApplication.isBackgroundUpdateCheckEnabled MainApplication.isDeveloper && !MainApplication.IS_WRAPPED && MainApplication.isBackgroundUpdateCheckEnabled
debugNotification.onPreferenceClickListener = debugNotification.onPreferenceClickListener =
Preference.OnPreferenceClickListener { _: Preference? -> Preference.OnPreferenceClickListener { _: Preference? ->
// fake updatable modules hashmap // fake updatable modules hashmap
@ -94,7 +95,7 @@ class UpdateFragment : PreferenceFragmentCompat() {
true true
} }
val backgroundUpdateCheck = findPreference<Preference>("pref_background_update_check") val backgroundUpdateCheck = findPreference<Preference>("pref_background_update_check")
backgroundUpdateCheck!!.isVisible = !MainApplication.isWrapped backgroundUpdateCheck!!.isVisible = !MainApplication.IS_WRAPPED
// Make uncheckable if POST_NOTIFICATIONS permission is not granted // Make uncheckable if POST_NOTIFICATIONS permission is not granted
if (!MainApplication.isNotificationPermissionGranted) { if (!MainApplication.isNotificationPermissionGranted) {
// Instead of disabling the preference, we make it uncheckable and when the user // Instead of disabling the preference, we make it uncheckable and when the user
@ -104,8 +105,9 @@ class UpdateFragment : PreferenceFragmentCompat() {
// set the box to unchecked // set the box to unchecked
(backgroundUpdateCheck as SwitchPreferenceCompat?)!!.isChecked = false (backgroundUpdateCheck as SwitchPreferenceCompat?)!!.isChecked = false
// ensure that the preference is false // ensure that the preference is false
MainApplication.getPreferences("mmm")!!.edit() MainApplication.getPreferences("mmm")!!.edit {
.putBoolean("pref_background_update_check", false).apply() putBoolean("pref_background_update_check", false)
}
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.permission_notification_title) MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.permission_notification_title)
.setMessage( .setMessage(
R.string.permission_notification_message R.string.permission_notification_message
@ -123,15 +125,15 @@ class UpdateFragment : PreferenceFragmentCompat() {
backgroundUpdateCheck.setSummary(R.string.background_update_check_permission_required) backgroundUpdateCheck.setSummary(R.string.background_update_check_permission_required)
} }
updateCheckExcludes!!.isVisible = updateCheckExcludes!!.isVisible =
MainApplication.isBackgroundUpdateCheckEnabled && !MainApplication.isWrapped MainApplication.isBackgroundUpdateCheckEnabled && !MainApplication.IS_WRAPPED
backgroundUpdateCheck.onPreferenceChangeListener = backgroundUpdateCheck.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any -> Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
val enabled = java.lang.Boolean.parseBoolean(newValue.toString()) val enabled = java.lang.Boolean.parseBoolean(newValue.toString())
debugNotification.isEnabled = enabled debugNotification.isEnabled = enabled
debugNotification.isVisible = debugNotification.isVisible =
MainApplication.isDeveloper && !MainApplication.isWrapped && enabled MainApplication.isDeveloper && !MainApplication.IS_WRAPPED && enabled
updateCheckExcludes.isEnabled = enabled updateCheckExcludes.isEnabled = enabled
updateCheckExcludes.isVisible = enabled && !MainApplication.isWrapped updateCheckExcludes.isVisible = enabled && !MainApplication.IS_WRAPPED
if (!enabled) { if (!enabled) {
BackgroundUpdateChecker.onMainActivityResume(requireContext()) BackgroundUpdateChecker.onMainActivityResume(requireContext())
} }
@ -183,9 +185,11 @@ class UpdateFragment : PreferenceFragmentCompat() {
stringSet.remove(id) stringSet.remove(id)
} }
} }
sharedPreferences.edit().putStringSet( sharedPreferences.edit {
"pref_background_update_check_excludes", stringSet putStringSet(
).apply() "pref_background_update_check_excludes", stringSet
)
}
}.setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> }.show() }.setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> }.show()
} else { } else {
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.background_update_check_excludes) MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.background_update_check_excludes)
@ -197,7 +201,7 @@ class UpdateFragment : PreferenceFragmentCompat() {
} }
// now handle pref_background_update_check_excludes_version // now handle pref_background_update_check_excludes_version
updateCheckVersionExcludes!!.isVisible = updateCheckVersionExcludes!!.isVisible =
MainApplication.isBackgroundUpdateCheckEnabled && !MainApplication.isWrapped MainApplication.isBackgroundUpdateCheckEnabled && !MainApplication.IS_WRAPPED
updateCheckVersionExcludes.onPreferenceClickListener = updateCheckVersionExcludes.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
// get the stringset pref_background_update_check_excludes_version // get the stringset pref_background_update_check_excludes_version
@ -291,9 +295,11 @@ class UpdateFragment : PreferenceFragmentCompat() {
if (MainApplication.forceDebugLogging) Timber.d("text is empty for %s", editText.hint.toString()) if (MainApplication.forceDebugLogging) Timber.d("text is empty for %s", editText.hint.toString())
} }
} }
sharedPreferences.edit().putStringSet( sharedPreferences.edit {
"pref_background_update_check_excludes_version", stringSetTemp putStringSet(
).apply() "pref_background_update_check_excludes_version", stringSetTemp
)
}
}.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> } }.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
.show() .show()
} }
@ -331,7 +337,7 @@ class UpdateFragment : PreferenceFragmentCompat() {
val debugDownload = val debugDownload =
findPreference<Preference>("pref_background_update_check_debug_download") findPreference<Preference>("pref_background_update_check_debug_download")
debugDownload!!.isVisible = debugDownload!!.isVisible =
MainApplication.isDeveloper && MainApplication.isBackgroundUpdateCheckEnabled && !MainApplication.isWrapped MainApplication.isDeveloper && MainApplication.isBackgroundUpdateCheckEnabled && !MainApplication.IS_WRAPPED
debugDownload.onPreferenceClickListener = debugDownload.onPreferenceClickListener =
Preference.OnPreferenceClickListener { _: Preference? -> Preference.OnPreferenceClickListener { _: Preference? ->
val intent = Intent(requireContext(), UpdateActivity::class.java) val intent = Intent(requireContext(), UpdateActivity::class.java)

@ -81,7 +81,7 @@ enum class IntentHelper {;
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
if (MainApplication.forceDebugLogging) Timber.d(e, "Could not find suitable activity to handle url") if (MainApplication.forceDebugLogging) Timber.d(e, "Could not find suitable activity to handle url")
Toast.makeText( Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString( context, MainApplication.getInstance().lastActivity!!.getString(
R.string.no_browser R.string.no_browser
), Toast.LENGTH_LONG ), Toast.LENGTH_LONG
).show() ).show()
@ -100,7 +100,7 @@ enum class IntentHelper {;
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
if (MainApplication.forceDebugLogging) Timber.d(e, "Could not find suitable activity to handle url") if (MainApplication.forceDebugLogging) Timber.d(e, "Could not find suitable activity to handle url")
Toast.makeText( Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString( context, MainApplication.getInstance().lastActivity!!.getString(
R.string.no_browser R.string.no_browser
), Toast.LENGTH_LONG ), Toast.LENGTH_LONG
).show() ).show()
@ -113,15 +113,15 @@ enum class IntentHelper {;
val request = DownloadManager.Request(Uri.parse(url)) val request = DownloadManager.Request(Uri.parse(url))
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE) .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
.setTitle((title?.replace(" ", "_") ?: "Module") + ".zip") .setTitle((title?.replace(" ", "_") ?: "Module") + ".zip")
.setDescription(MainApplication.INSTANCE!!.lastActivity!!.getString(R.string.download_module_description, title)) .setDescription(MainApplication.getInstance().lastActivity!!.getString(R.string.download_module_description, title))
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setAllowedOverMetered(true) .setAllowedOverMetered(true)
.setAllowedOverRoaming(false) .setAllowedOverRoaming(false)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, (title?.replace(" ", "_") ?: "Module") + ".zip") .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, (title?.replace(" ", "_") ?: "Module") + ".zip")
val downloadManager= getSystemService(MainApplication.INSTANCE!!.lastActivity!!.applicationContext, DownloadManager::class.java)!! val downloadManager= getSystemService(MainApplication.getInstance().lastActivity!!.applicationContext, DownloadManager::class.java)!!
val downloadID = downloadManager.enqueue(request) val downloadID = downloadManager.enqueue(request)
Toast.makeText( Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString( context, MainApplication.getInstance().lastActivity!!.getString(
R.string.download_started R.string.download_started
), Toast.LENGTH_LONG ), Toast.LENGTH_LONG
).show() ).show()
@ -136,7 +136,7 @@ enum class IntentHelper {;
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_FAILED) { if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_FAILED) {
downloading = false downloading = false
Toast.makeText( Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString( context, MainApplication.getInstance().lastActivity!!.getString(
R.string.download_failed R.string.download_failed
), Toast.LENGTH_LONG ), Toast.LENGTH_LONG
).show() ).show()
@ -144,7 +144,7 @@ enum class IntentHelper {;
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) { if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
downloading = false downloading = false
Toast.makeText( Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString( context, MainApplication.getInstance().lastActivity!!.getString(
R.string.download_finished R.string.download_finished
), Toast.LENGTH_LONG ), Toast.LENGTH_LONG
).show() ).show()
@ -357,7 +357,7 @@ enum class IntentHelper {;
intent1.putExtra(EXTRA_TAB_TOOLBAR_COLOR, typedValue.data) intent1.putExtra(EXTRA_TAB_TOOLBAR_COLOR, typedValue.data)
intent1.putExtra( intent1.putExtra(
EXTRA_TAB_COLOR_SCHEME, EXTRA_TAB_COLOR_SCHEME,
if (MainApplication.INSTANCE!!.isLightTheme) EXTRA_TAB_COLOR_SCHEME_LIGHT else EXTRA_TAB_COLOR_SCHEME_DARK if (MainApplication.getInstance().isLightTheme) EXTRA_TAB_COLOR_SCHEME_LIGHT else EXTRA_TAB_COLOR_SCHEME_DARK
) )
} }
} }

@ -46,7 +46,7 @@ class RuntimeUtils {
) != PackageManager.PERMISSION_GRANTED ) != PackageManager.PERMISSION_GRANTED
) { ) {
if (MainApplication.forceDebugLogging) Timber.i("Request Notification Permission") if (MainApplication.forceDebugLogging) Timber.i("Request Notification Permission")
if (MainApplication.INSTANCE!!.lastActivity!! if (MainApplication.getInstance().lastActivity!!
.shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) .shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
) { ) {
// Show a dialog explaining why we need context permission, which is to show // Show a dialog explaining why we need context permission, which is to show

@ -20,7 +20,7 @@ class FileUtils {
try { try {
FileUtils.forceMkdir( FileUtils.forceMkdir(
File( File(
(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm").replace( (MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm").replace(
"//".toRegex(), "//".toRegex(),
"/" "/"
) )
@ -28,7 +28,7 @@ class FileUtils {
) )
FileUtils.forceMkdir( FileUtils.forceMkdir(
File( File(
(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js").replace( (MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js").replace(
"//".toRegex(), "//".toRegex(),
"/" "/"
) )
@ -36,7 +36,7 @@ class FileUtils {
) )
FileUtils.forceMkdir( FileUtils.forceMkdir(
File( File(
(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/cronet").replace( (MainApplication.getInstance().dataDir.toString() + "/cache/cronet").replace(
"//".toRegex(), "//".toRegex(),
"/" "/"
) )

@ -207,7 +207,7 @@ enum class Files {
fun fixSourceArchiveShit(rawModule: ByteArray?) { fun fixSourceArchiveShit(rawModule: ByteArray?) {
// unzip the module, check if it has just one folder within. if so, switch to the folder and zip up contents, and replace the original file with that // unzip the module, check if it has just one folder within. if so, switch to the folder and zip up contents, and replace the original file with that
try { try {
val tempDir = File(MainApplication.INSTANCE!!.cacheDir, "temp") val tempDir = File(MainApplication.getInstance().cacheDir, "temp")
if (tempDir.exists()) { if (tempDir.exists()) {
FileUtils.deleteDirectory(tempDir) FileUtils.deleteDirectory(tempDir)
} }

@ -12,13 +12,14 @@ import android.content.SharedPreferences
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.Network import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.Uri
import android.os.Build import android.os.Build
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebSettings import android.webkit.WebSettings
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.webkit.WebViewCompat import androidx.webkit.WebViewCompat
import com.fox2code.mmm.BuildConfig import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity import com.fox2code.mmm.MainActivity
@ -30,30 +31,19 @@ import com.fox2code.mmm.installer.InstallerInitializer.Companion.peekMagiskVersi
import com.fox2code.mmm.utils.io.Files.Companion.makeBuffer import com.fox2code.mmm.utils.io.Files.Companion.makeBuffer
import com.google.net.cronet.okhttptransport.CronetInterceptor import com.google.net.cronet.okhttptransport.CronetInterceptor
import ly.count.android.sdk.Countly import ly.count.android.sdk.Countly
import okhttp3.Cache
import okhttp3.Dns import okhttp3.Dns
import okhttp3.HttpUrl.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Interceptor.Chain.*
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.OkHttpClient.Builder.*
import okhttp3.Request import okhttp3.Request
import okhttp3.Request.*
import okhttp3.Request.Builder.*
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.Response import okhttp3.Response
import okhttp3.Response.*
import okhttp3.dnsoverhttps.DnsOverHttps
import okhttp3.dnsoverhttps.DnsOverHttps.Builder.*
import okio.BufferedSink import okio.BufferedSink
import org.chromium.net.CronetEngine import org.chromium.net.CronetEngine
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.Long.*
import java.net.InetAddress import java.net.InetAddress
import java.net.Proxy import java.net.Proxy
import java.net.UnknownHostException import java.net.UnknownHostException
@ -78,7 +68,8 @@ enum class Http {;
*/ */
private class FallBackDNS(parent: Dns, vararg fallbacks: String?) : Dns { private class FallBackDNS(parent: Dns, vararg fallbacks: String?) : Dns {
private val parent: Dns private val parent: Dns
private val sharedPreferences: SharedPreferences = MainApplication.getPreferences("mmm_dns")!! private val sharedPreferences: SharedPreferences =
MainApplication.getPreferences("mmm_dns")!!
private val fallbacks: HashSet<String> private val fallbacks: HashSet<String>
private val fallbackCache: HashMap<String, List<InetAddress>> private val fallbackCache: HashMap<String, List<InetAddress>>
@ -104,8 +95,9 @@ enum class Http {;
hostname hostname
) )
fallbackCache[hostname] = addresses fallbackCache[hostname] = addresses
sharedPreferences.edit() sharedPreferences.edit {
.putString(hostname.replace('.', '_'), toString(addresses)).apply() putString(hostname.replace('.', '_'), toString(addresses))
}
} catch (e: UnknownHostException) { } catch (e: UnknownHostException) {
val key = sharedPreferences.getString(hostname.replace('.', '_'), "") val key = sharedPreferences.getString(hostname.replace('.', '_'), "")
if (key!!.isEmpty()) throw e if (key!!.isEmpty()) throw e
@ -113,7 +105,7 @@ enum class Http {;
addresses = fromString(key) addresses = fromString(key)
fallbackCache.put(hostname, addresses) fallbackCache.put(hostname, addresses)
} catch (e2: UnknownHostException) { } catch (e2: UnknownHostException) {
sharedPreferences.edit().remove(hostname.replace('.', '_')).apply() sharedPreferences.edit { remove(hostname.replace('.', '_')) }
throw e throw e
} }
} }
@ -197,7 +189,7 @@ enum class Http {;
private var doh = false private var doh = false
init { init {
val mainApplication = MainApplication.INSTANCE val mainApplication = MainApplication.getInstance()
if (mainApplication == null) { if (mainApplication == null) {
val error = Error("Initialized Http too soon!") val error = Error("Initialized Http too soon!")
error.fillInStackTrace() error.fillInStackTrace()
@ -230,7 +222,7 @@ enum class Http {;
var webviewVersion = "0.0.0" var webviewVersion = "0.0.0"
val pi = WebViewCompat.getCurrentWebViewPackage(mainApplication) val pi = WebViewCompat.getCurrentWebViewPackage(mainApplication)
if (pi != null) { if (pi != null) {
webviewVersion = pi.versionName webviewVersion = pi.versionName ?: "0.0.0"
} }
// webviewVersionMajor is the everything before the first dot // webviewVersionMajor is the everything before the first dot
val webviewVersionCode: Int val webviewVersionCode: Int
@ -257,8 +249,8 @@ enum class Http {;
httpclientBuilder.writeTimeout(5, TimeUnit.SECONDS) httpclientBuilder.writeTimeout(5, TimeUnit.SECONDS)
httpclientBuilder.readTimeout(5, TimeUnit.SECONDS) httpclientBuilder.readTimeout(5, TimeUnit.SECONDS)
httpclientBuilder.proxy(Proxy.NO_PROXY) // Do not use system proxy httpclientBuilder.proxy(Proxy.NO_PROXY) // Do not use system proxy
// TODO: Make compatible w/ cronet // TODO: Make compatible w/ cronet
// var dns = Dns.SYSTEM // var dns = Dns.SYSTEM
try { try {
val cookieJar = WebkitCookieManagerProxy() val cookieJar = WebkitCookieManagerProxy()
httpclientBuilder.cookieJar(cookieJar) httpclientBuilder.cookieJar(cookieJar)
@ -269,7 +261,10 @@ enum class Http {;
} }
// User-Agent format was agreed on telegram // User-Agent format was agreed on telegram
androidacyUA = if (hasWebView) { androidacyUA = if (hasWebView) {
WebSettings.getDefaultUserAgent(mainApplication).replaceFirst("(; )?wv".toRegex(), "").replaceFirst(" Version/[^ ]*".toRegex(), "") + " AMM/" + BuildConfig.VERSION_CODE WebSettings.getDefaultUserAgent(mainApplication)
.replaceFirst("(; )?wv".toRegex(), "").replaceFirst(
" Version/[^ ]*".toRegex(), ""
) + " AMM/" + BuildConfig.VERSION_CODE
} else { } else {
"Mozilla/5.0 (Linux; Android " + Build.VERSION.RELEASE + "; " + Build.DEVICE + ")" + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Mobile Safari/537.36" + " AMMM/" + BuildConfig.VERSION_CODE "Mozilla/5.0 (Linux; Android " + Build.VERSION.RELEASE + "; " + Build.DEVICE + ")" + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Mobile Safari/537.36" + " AMMM/" + BuildConfig.VERSION_CODE
} }
@ -360,21 +355,21 @@ enum class Http {;
// Gracefully fallback to okhttp // Gracefully fallback to okhttp
} }
// Fallback DNS cache responses in case request fail but already succeeded once in the past // Fallback DNS cache responses in case request fail but already succeeded once in the past
/* fallbackDNS = FallBackDNS( /* fallbackDNS = FallBackDNS(
dns, dns,
"github.com", "github.com",
"api.github.com", "api.github.com",
"raw.githubusercontent.com", "raw.githubusercontent.com",
"camo.githubusercontent.com", "camo.githubusercontent.com",
"user-images.githubusercontent.com", "user-images.githubusercontent.com",
"cdn.jsdelivr.net", "cdn.jsdelivr.net",
"img.shields.io", "img.shields.io",
"magisk-modules-repo.github.io", "magisk-modules-repo.github.io",
"www.androidacy.com", "www.androidacy.com",
"api.androidacy.com", "api.androidacy.com",
"production-api.androidacy.com" "production-api.androidacy.com"
) )
httpclientBuilder.dns(Dns.SYSTEM)*/ httpclientBuilder.dns(Dns.SYSTEM)*/
httpClient = followRedirects(httpclientBuilder, true).build() httpClient = followRedirects(httpclientBuilder, true).build()
followRedirects(httpclientBuilder, false).build() followRedirects(httpclientBuilder, false).build()
httpClientDoH = followRedirects(httpclientBuilder, true).build() httpClientDoH = followRedirects(httpclientBuilder, true).build()
@ -400,7 +395,7 @@ enum class Http {;
private fun checkNeedCaptchaAndroidacy(url: String, errorCode: Int) { private fun checkNeedCaptchaAndroidacy(url: String, errorCode: Int) {
if (errorCode == 403 && AndroidacyUtil.isAndroidacyLink(url)) { if (errorCode == 403 && AndroidacyUtil.isAndroidacyLink(url)) {
needCaptchaAndroidacyHost = Uri.parse(url).host needCaptchaAndroidacyHost = url.toUri().host
} }
} }
@ -437,10 +432,10 @@ enum class Http {;
Timber.e(e, "Failed to get %s", url) Timber.e(e, "Failed to get %s", url)
// detect ssl errors, i.e., cert authority invalid by looking at the message // detect ssl errors, i.e., cert authority invalid by looking at the message
if (e.message != null && e.message!!.contains("_CERT_")) { if (e.message != null && e.message!!.contains("_CERT_")) {
MainApplication.INSTANCE!!.lastActivity!!.runOnUiThread { MainApplication.getInstance().lastActivity!!.runOnUiThread {
// show toast // show toast
Toast.makeText( Toast.makeText(
MainApplication.INSTANCE, R.string.ssl_error, Toast.LENGTH_LONG MainApplication.getInstance(), R.string.ssl_error, Toast.LENGTH_LONG
).show() ).show()
} }
} }
@ -495,9 +490,13 @@ enum class Http {;
} }
} }
if (BuildConfig.DEBUG_HTTP) { if (BuildConfig.DEBUG_HTTP) {
if (MainApplication.forceDebugLogging) Timber.d("doHttpGet: " + url.replace("=[^&]*".toRegex(), "=****") + " succeeded") if (MainApplication.forceDebugLogging) Timber.d(
"%s succeeded", url.replace(
"=[^&]*".toRegex(), "=****"
)
)
} }
var responseBody = response?.body var responseBody = response.body
// Use cache api if used cached response // Use cache api if used cached response
if (response != null) { if (response != null) {
if (response.code == 304) { if (response.code == 304) {
@ -507,12 +506,15 @@ enum class Http {;
} }
if (BuildConfig.DEBUG_HTTP) { if (BuildConfig.DEBUG_HTTP) {
if (responseBody != null) { if (responseBody != null) {
if (MainApplication.forceDebugLogging) Timber.d("doHttpGet: returning " + responseBody.contentLength() + " bytes") if (MainApplication.forceDebugLogging) Timber.d(
"%s bytes doHttpGet: returning",
responseBody.contentLength()
)
} }
} }
if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) { if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
Countly.sharedInstance().apm().endNetworkRequest(uniqid, url, response!!.code, Countly.sharedInstance().apm().endNetworkRequest(
0, responseBody!!.contentLength().toInt() uniqid, url, response!!.code, 0, responseBody.contentLength().toInt()
) )
} }
return responseBody?.bytes() ?: ByteArray(0) return responseBody?.bytes() ?: ByteArray(0)
@ -544,10 +546,10 @@ enum class Http {;
Timber.e(e, "Failed to post %s", url) Timber.e(e, "Failed to post %s", url)
// detect ssl errors, i.e., cert authority invalid by looking at the message // detect ssl errors, i.e., cert authority invalid by looking at the message
if (e.message != null && e.message!!.contains("_CERT_")) { if (e.message != null && e.message!!.contains("_CERT_")) {
MainApplication.INSTANCE!!.lastActivity!!.runOnUiThread { MainApplication.getInstance().lastActivity!!.runOnUiThread {
// show toast // show toast
Toast.makeText( Toast.makeText(
MainApplication.INSTANCE, R.string.ssl_error, Toast.LENGTH_LONG MainApplication.getInstance(), R.string.ssl_error, Toast.LENGTH_LONG
).show() ).show()
} }
} }
@ -555,7 +557,9 @@ enum class Http {;
} }
if (response.isRedirect) { if (response.isRedirect) {
// follow redirect with same method // follow redirect with same method
if (MainApplication.forceDebugLogging) Timber.d("doHttpPostRaw: following redirect: %s", response.header("Location")) if (MainApplication.forceDebugLogging) Timber.d(
"doHttpPostRaw: following redirect: %s", response.header("Location")
)
response = response =
(if (allowCache) getHttpClientWithCache() else getHttpClient())!!.newCall( (if (allowCache) getHttpClientWithCache() else getHttpClient())!!.newCall(
Request.Builder().url( Request.Builder().url(
@ -606,8 +610,12 @@ enum class Http {;
if (response != null) responseBody = response.body if (response != null) responseBody = response.body
} }
if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) { if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
Countly.sharedInstance().apm().endNetworkRequest(uniqid, url, response!!.code, Countly.sharedInstance().apm().endNetworkRequest(
data.toByteArray().size.toLong().toInt(), responseBody.contentLength().toInt() uniqid,
url,
response!!.code,
data.toByteArray().size.toLong().toInt(),
responseBody.contentLength().toInt()
) )
} }
return responseBody.bytes() return responseBody.bytes()
@ -627,10 +635,10 @@ enum class Http {;
Timber.e(e, "Failed to post %s", url) Timber.e(e, "Failed to post %s", url)
// detect ssl errors, i.e., cert authority invalid by looking at the message // detect ssl errors, i.e., cert authority invalid by looking at the message
if (e.message != null && e.message!!.contains("_CERT_")) { if (e.message != null && e.message!!.contains("_CERT_")) {
MainApplication.INSTANCE!!.lastActivity!!.runOnUiThread { MainApplication.getInstance().lastActivity!!.runOnUiThread {
// show toast // show toast
Toast.makeText( Toast.makeText(
MainApplication.INSTANCE, R.string.ssl_error, Toast.LENGTH_LONG MainApplication.getInstance(), R.string.ssl_error, Toast.LENGTH_LONG
).show() ).show()
} }
} }
@ -703,8 +711,8 @@ enum class Http {;
(downloaded / divider).toInt(), (target / divider).toInt(), true (downloaded / divider).toInt(), (target / divider).toInt(), true
) )
if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) { if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
Countly.sharedInstance().apm().endNetworkRequest(uniqid, url, response.code, Countly.sharedInstance().apm().endNetworkRequest(
0, responseBody.contentLength().toInt() uniqid, url, response.code, 0, responseBody.contentLength().toInt()
) )
} }
return byteArrayOutputStream.toByteArray() return byteArrayOutputStream.toByteArray()
@ -778,8 +786,7 @@ enum class Http {;
} }
if (MainApplication.forceDebugLogging) Timber.d("We say we have internet: $hasInternet") if (MainApplication.forceDebugLogging) Timber.d("We say we have internet: $hasInternet")
lastConnectivityCheck = System.currentTimeMillis() lastConnectivityCheck = System.currentTimeMillis()
@Suppress("KotlinConstantConditions") @Suppress("KotlinConstantConditions") lastConnectivityResult =
lastConnectivityResult =
systemSaysYes && hasInternet systemSaysYes && hasInternet
return lastConnectivityResult return lastConnectivityResult
} }

Loading…
Cancel
Save