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 3 months 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!
**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)
## Overview and Purpose

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

@ -19,7 +19,7 @@ class AppUpdateManager private constructor() {
private var changes: String? = null
private val compatDataId = HashMap<String, Int>()
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 lastChecked: Long

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

@ -78,9 +78,8 @@ import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Files.*
import java.sql.Timestamp
import kotlin.math.roundToInt
import androidx.core.view.isVisible
class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
@ -134,10 +133,9 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
if (ContentResolver.SCHEME_FILE == uri.scheme) {
var path = uri.path
if (path!!.startsWith("/sdcard/")) { // Fix file paths
path =
Environment.getExternalStorageDirectory().absolutePath + path.substring(
7
)
path = Environment.getExternalStorageDirectory().absolutePath + path.substring(
7
)
}
inputStream = SuFileInputStream.open(
File(path).absoluteFile
@ -151,21 +149,17 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
this@MainActivity, R.string.file_picker_failure, Toast.LENGTH_SHORT
).show()
callback?.onReceived(
destination,
uri,
IntentHelper.RESPONSE_ERROR
destination, uri, IntentHelper.RESPONSE_ERROR
)
return@registerForActivityResult
}
run {
outputStream = FileOutputStream(destination)
Files.copy(inputStream, outputStream as FileOutputStream)
Files.copy(inputStream, outputStream)
if (MainApplication.forceDebugLogging) Timber.i("File saved at %s", destination)
success = true
callback?.onReceived(
destination,
uri,
IntentHelper.RESPONSE_FILE
destination, uri, IntentHelper.RESPONSE_FILE
)
}
} catch (e: Exception) {
@ -189,7 +183,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
super.onResume()
onMainActivityResume(this)
// 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
} else {
bottomNavigationView.selectedItemId = R.id.online_menu_item
@ -225,14 +219,14 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
return
}
// hide this behind a buildconfig flag for now, but crash the app if it's not an official build and not debug
if (BuildConfig.ENABLE_PROTECTION && !MainApplication.o && !BuildConfig.DEBUG) {
if (!MainApplication.o && !BuildConfig.DEBUG) {
throw RuntimeException("This is not an official build of AMM")
} else if (!MainApplication.o && !BuildConfig.DEBUG) {
Timber.w("You may be running an untrusted build.")
// Show a toast to warn the user
Toast.makeText(this, R.string.not_official_build, Toast.LENGTH_LONG).show()
}
// track enabled repos
Thread {
val db = Room.databaseBuilder(
@ -254,32 +248,11 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
repoMap["repos"] = enabledRepos.toString()
if (MainApplication.analyticsAllowed()) Countly.sharedInstance().events()
.recordEvent(
"enabled_repos",
repoMap as Map<String, Any>?, 1
"enabled_repos", repoMap as Map<String, Any>?, 1
)
}
}.start()
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000)
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()
}
}
MainApplication.getInstance().check(this)
setContentView(R.layout.activity_main)
this.setTitle(R.string.app_name_v2)
// set navigation bar color based on surfacecolors
@ -300,7 +273,8 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
val view = findViewById<View>(R.id.root_container)
var startBottom = 0f
var endBottom = 0f
ViewCompat.setWindowInsetsAnimationCallback(view,
ViewCompat.setWindowInsetsAnimationCallback(
view,
object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
// Override methods…
override fun onProgress(
@ -357,8 +331,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
Thread {
if (moduleViewListBuilder.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on offline list",
query
"Query submit: %s on offline list", query
)
Thread(
{ moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!) },
@ -368,8 +341,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
// same for online list
if (moduleViewListBuilderOnline.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on online list",
query
"Query submit: %s on online list", query
)
Thread({
moduleViewListBuilderOnline.applyTo(
@ -392,8 +364,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
Thread {
if (moduleViewListBuilder.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on offline list",
query
"Query submit: %s on offline list", query
)
Thread(
{ moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!) },
@ -403,8 +374,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
// same for online list
if (moduleViewListBuilderOnline.setQueryChange(query)) {
if (MainApplication.forceDebugLogging) Timber.i(
"Query submit: %s on online list",
query
"Query submit: %s on online list", query
)
Thread({
moduleViewListBuilderOnline.applyTo(
@ -460,8 +430,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
) { _: DialogInterface?, which: Int ->
when (which) {
0 -> RuntimeUtils.reboot(
this@MainActivity,
RuntimeUtils.RebootMode.REBOOT
this@MainActivity, RuntimeUtils.RebootMode.REBOOT
)
1 -> RuntimeUtils.reboot(
@ -597,7 +566,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
bottomNavigationView.selectedItemId = R.id.installed_menu_item
}
// reset update module and update module count in main application
MainApplication.INSTANCE!!.resetUpdateModule()
MainApplication.getInstance().resetUpdateModule()
tryGetMagiskPathAsync(object : InstallerInitializer.Callback {
override fun onPathReceived(path: String?) {
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
runOnUiThread(if (max == 0) Runnable {
progressIndicator.setProgress(
80,
true
80, true
)
} else Runnable {
progressIndicator.setProgress(
30 + value,
true
30 + value, 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
runOnUiThread {
progressIndicator.setProgress(
80 + (currentTmp / max.toFloat() * 20).roundToInt(),
true
80 + (currentTmp / max.toFloat() * 20).roundToInt(), true
)
if (BuildConfig.DEBUG) {
Timber.i(
@ -764,13 +730,13 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
moduleViewListBuilderOnline.applyTo(moduleListOnline, moduleViewAdapterOnline!!)
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter!!)
// 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")
Handler(Looper.getMainLooper()).post {
val badge = bottomNavigationView.getOrCreateBadge(R.id.online_menu_item)
badge.isVisible = true
badge.number = MainApplication.INSTANCE!!.updateModuleCount
badge.applyTheme(MainApplication.INSTANCE!!.theme)
badge.number = MainApplication.getInstance().updateModuleCount
badge.applyTheme(MainApplication.getInstance().theme)
if (MainApplication.forceDebugLogging) Timber.i("Badge applied")
}
}
@ -800,11 +766,10 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
if (MainApplication.analyticsAllowed()) Countly.sharedInstance().feedback()
.getAvailableFeedbackWidgets { retrievedWidgets, error ->
if (MainApplication.forceDebugLogging) Timber.i(
"Got feedback widgets: %s",
retrievedWidgets.size
"Got feedback widgets: %s", retrievedWidgets.size
)
if (error == null) {
if (retrievedWidgets.size > 0) {
if (retrievedWidgets.isNotEmpty()) {
val feedbackWidget = retrievedWidgets[0]
if (MainApplication.analyticsAllowed()) Countly.sharedInstance().feedback()
.presentFeedbackWidget(
@ -870,7 +835,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
progressIndicator!!.setProgress(20, true)
swipeRefreshBlocker = System.currentTimeMillis() + 5000L
MainApplication.INSTANCE!!.repoModules.clear()
MainApplication.getInstance().repoModules.clear()
// this.swipeRefreshLayout.setRefreshing(true); ??
Thread({
cleanDnsCache() // Allow DNS reload from network
@ -884,13 +849,11 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
} else Runnable {
progressIndicator!!.setProgress(
// going from 30 to 80 as evenly as possible
30 + value,
true
30 + value, true
)
if (BuildConfig.DEBUG) {
Timber.i(
"Progress: %d",
30 + value
"Progress: %d", 30 + value
)
}
})
@ -978,8 +941,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
runtimeUtils!!.showUpgradeSnackbar(this, this)
} else {
if (AndroidacyRepoData.instance.memberLevel == null || !AndroidacyRepoData.instance.memberLevel.equals(
"Guest",
ignoreCase = true
"Guest", ignoreCase = true
)
) {
if (MainApplication.forceDebugLogging) Timber.i(
@ -1015,7 +977,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
v.clearFocus()
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.SystemClock
import android.util.Log
import android.widget.Toast
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.edit
import androidx.emoji2.text.DefaultEmojiCompatConfig
import androidx.emoji2.text.EmojiCompat
import androidx.security.crypto.EncryptedSharedPreferences
@ -103,6 +105,62 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
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
@SuppressLint("ApplySharedPref")
fun getKey(): CharArray {
@ -130,7 +188,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
for (i in newKey.indices) {
newKey[i] = (random.nextInt(26) + 97).toChar()
}
sharedPreferences.edit().putString("dbKey", String(newKey)).commit()
sharedPreferences.edit(commit = true) { putString("dbKey", String(newKey)) }
existingKey = newKey
return existingKey!!
}
@ -241,8 +299,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
if (forceDebugLogging) Timber.d("Root access granted")
} else {
if (forceDebugLogging) Timber.d(
"Root access or we're not uid 0. Current uid: %s",
output
"Root access or we're not uid 0. Current uid: %s", output
)
}
}
@ -280,18 +337,16 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
if (forceDebugLogging) Timber.d("countly is not allowed")
} else {
val config = CountlyConfig(
this,
"e1cad244769e6fa56ad2d3fcb42e439e772827d1",
"https://cly.androidacy.com"
this, "e1cad244769e6fa56ad2d3fcb42e439e772827d1", "https://cly.androidacy.com"
)
if (isCrashReportingEnabled) {
config.enableCrashReporting()
config.crashes.enableCrashReporting()
}
config.enableAutomaticViewTracking()
config.setPushIntentAddMetadata(true)
config.setLoggingEnabled(BuildConfig.DEBUG)
config.setRequiresConsent(false)
config.setRecordAppStartTime(true)
config.apm.enableAppStartTimeTracking()
config.enableServerConfiguration()
Countly.sharedInstance().init(config)
Countly.applicationOnCreate()
@ -307,7 +362,8 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
"e8ce7deca880304d7ff09f8fc37656cfa927cee7f6a0bb7b3feda6a5942931f5",
"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)
} catch (ignored: PackageManager.NameNotFoundException) {
}
@ -318,10 +374,11 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
val lastBootPrefs = bootPrefs!!.getLong("last_boot", 0)
isFirstBoot = if (lastBootPrefs == 0L || abs(lastBoot - lastBootPrefs) > 100) {
val firstBoot = sharedPreferences!!.getBoolean("first_boot", true)
bootPrefs.edit().clear().putLong("last_boot", lastBoot)
.putBoolean("first_boot", firstBoot).apply()
bootPrefs.edit {
clear().putLong("last_boot", lastBoot).putBoolean("first_boot", firstBoot)
}
if (firstBoot) {
sharedPreferences.edit().putBoolean("first_boot", false).apply()
sharedPreferences.edit { putBoolean("first_boot", false) }
}
firstBoot
} else {
@ -348,10 +405,10 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
}
@Suppress("KotlinConstantConditions") if ((BuildConfig.ANDROIDACY_CLIENT_ID == "")) {
Timber.w("Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy.")
val editor = sharedPreferences!!.edit()
editor.putBoolean("pref_androidacy_repo_enabled", false)
Timber.w("ANDROIDACY_CLIENT_ID is empty, disabling AndroidacyRepoData 1")
editor.apply()
sharedPreferences!!.edit {
putBoolean("pref_androidacy_repo_enabled", false)
Timber.w("ANDROIDACY_CLIENT_ID is empty, disabling AndroidacyRepoData 1")
}
}
}
@ -457,7 +514,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
// basically silently drop all logs below error, and write the rest to logcat
@Suppress("NAME_SHADOWING") var message = message
if (INSTANCE!!.isTainted) {
if (getInstance().isTainted) {
// prepend [TAINTED] to the message
message = "[TAINTED] $message"
}
@ -481,12 +538,13 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
}
companion object {
fun getInstance(): MainApplication {
return INSTANCE!!
}
var forceDebugLogging: Boolean =
BuildConfig.DEBUG || getPreferences("mmm")?.getBoolean(
"pref_force_debug_logging",
false
) ?: BuildConfig.DEBUG
var forceDebugLogging: Boolean = BuildConfig.DEBUG || getPreferences("mmm")?.getBoolean(
"pref_force_debug_logging", false
) ?: BuildConfig.DEBUG
// 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%
@ -497,7 +555,13 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
// Is application wrapped, and therefore must reduce it's feature set.
@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>()
@JvmField
@ -509,11 +573,10 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
private var relPackageName = BuildConfig.APPLICATION_ID
@SuppressLint("StaticFieldLeak")
var INSTANCE: MainApplication? = null
private set
private var INSTANCE: MainApplication? = null
get() {
if (field == null) {
Timber.w("MainApplication.INSTANCE is null, using fallback!")
Timber.w("MainApplication.getInstance() is null, using fallback!")
return null
}
return field
@ -525,8 +588,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
init {
Shell.setDefaultBuilder(
Shell.Builder.create()
.setFlags(Shell.FLAG_REDIRECT_STDERR or Shell.FLAG_MOUNT_MASTER).setTimeout(15)
Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER).setTimeout(15)
)
// set verbose logging for debug builds
if (BuildConfig.DEBUG) {
@ -564,15 +626,15 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
fun getPreferences(name: String): SharedPreferences? {
// encryptedSharedPreferences is used
return try {
var name = name
val mContext: Context? = INSTANCE!!.applicationContext
name += "x"
if (mSharedPrefs == null) {
mSharedPrefs = HashMap()
}
if (mSharedPrefs!!.containsKey(name)) {
return mSharedPrefs!![name] as SharedPreferences?
}
var name = name
val mContext: Context? = INSTANCE!!.applicationContext
name += "x"
if (mSharedPrefs == null) {
mSharedPrefs = HashMap()
}
if (mSharedPrefs!!.containsKey(name)) {
return mSharedPrefs!![name] as SharedPreferences?
}
val masterKey =
MasterKey.Builder(mContext!!).setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
@ -594,10 +656,8 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
} catch (ignored: InterruptedException) {
}
try {
val masterKey =
MasterKey.Builder(INSTANCE!!.applicationContext)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val masterKey = MasterKey.Builder(INSTANCE!!.applicationContext)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val sharedPreferences = EncryptedSharedPreferences.create(
INSTANCE!!.applicationContext,
name,
@ -631,8 +691,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
// convert from String to boolean
return java.lang.Boolean.parseBoolean(SHOWCASE_MODE_TRUE)
}
val showcaseMode =
getPreferences("mmm")!!.getBoolean("pref_showcase_mode", false)
val showcaseMode = getPreferences("mmm")!!.getBoolean("pref_showcase_mode", false)
SHOWCASE_MODE_TRUE = showcaseMode.toString()
return showcaseMode
}
@ -670,8 +729,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
get() = (peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND) && getPreferences(
"mmm"
)!!.getBoolean(
"pref_use_magisk_install_command",
false
"pref_use_magisk_install_command", false
) && isDeveloper && !InstallerInitializer.isKsu
val isBackgroundUpdateCheckEnabled: Boolean
@ -679,7 +737,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
if (updateCheckBg != null) {
return java.lang.Boolean.parseBoolean(updateCheckBg)
}
val wrapped = isWrapped
val wrapped = IS_WRAPPED
val updateCheckBgTemp = !wrapped && getPreferences("mmm")!!.getBoolean(
"pref_background_update_check", true
)
@ -692,7 +750,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
)
fun setHasGottenRootAccess(bool: Boolean) {
getPreferences("mmm")!!.edit().putBoolean("has_root_access", bool).apply()
getPreferences("mmm")!!.edit { putBoolean("has_root_access", bool) }
}
val isCrashReportingEnabled: Boolean
@ -724,9 +782,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
val randChance = Random().nextInt(5)
val lastShown = getPreferences("mmm")!!.getLong("last_feedback", 0)
if (forceDebugLogging) Timber.d(
"Last feedback shown: %d, randChance: %d",
lastShown,
randChance
"Last feedback shown: %d, randChance: %d", lastShown, randChance
)
return System.currentTimeMillis() - lastShown > 1209600000 && randChance == 0
}

@ -3,8 +3,7 @@
*/
@file:Suppress(
"KotlinConstantConditions",
"ktConcatNullable"
"KotlinConstantConditions", "ktConcatNullable"
)
package com.fox2code.mmm
@ -38,8 +37,8 @@ enum class NotificationType(
var special: Boolean = false
) : NotificationTypeCst {
@JvmStatic
DEBUG(R.string.debug_build,
DEBUG(
R.string.debug_build,
R.drawable.ic_baseline_bug_report_24,
com.google.android.material.R.attr.colorPrimary,
com.google.android.material.R.attr.colorOnPrimary,
@ -66,7 +65,6 @@ enum class NotificationType(
}
},
@JvmStatic
SHOWCASE_MODE(
R.string.showcase_mode,
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) {
override fun shouldRemove(): Boolean {
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,
View.OnClickListener { v: View ->
IntentHelper.openUrl(
@ -103,22 +102,23 @@ enum class NotificationType(
}
},
@JvmStatic
NO_INTERNET(R.string.fail_internet, R.drawable.ic_baseline_cloud_off_24) {
override fun shouldRemove(): Boolean {
return RepoManager.getINSTANCE()!!.hasConnectivity()
}
},
@JvmStatic
REPO_UPDATE_FAILED(R.string.repo_update_failed, R.drawable.ic_baseline_cloud_off_24) {
override fun shouldRemove(): Boolean {
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,
View.OnClickListener { v: View ->
IntentHelper.openUrlAndroidacy(
@ -130,14 +130,14 @@ enum class NotificationType(
}
},
@JvmStatic
NO_WEB_VIEW(R.string.no_web_view, R.drawable.ic_baseline_android_24) {
override fun shouldRemove(): Boolean {
return Http.hasWebView()
}
},
@JvmStatic
UPDATE_AVAILABLE(
R.string.app_update_available,
R.drawable.ic_baseline_system_update_24,
@ -166,7 +166,7 @@ enum class NotificationType(
}
},
@JvmStatic
INSTALL_FROM_STORAGE(
R.string.install_from_storage,
R.drawable.ic_baseline_storage_24,
@ -182,17 +182,14 @@ enum class NotificationType(
.setPositiveButton(android.R.string.ok, null).show()
return@OnClickListener
}
val compatActivity = MainApplication.INSTANCE!!.lastActivity!!
val compatActivity = MainApplication.getInstance().lastActivity!!
val module = File(
compatActivity.cacheDir, "installer" + File.separator + "module.zip"
)
IntentHelper.openFileTo(module, object :
OnFileReceivedCallback {
IntentHelper.openFileTo(module, object : OnFileReceivedCallback {
override fun onReceived(
target: File?,
uri: Uri?,
response: Int
target: File?, uri: Uri?, response: Int
) {
Companion
if (response == IntentHelper.RESPONSE_FILE) {
@ -206,28 +203,16 @@ enum class NotificationType(
return
}
IntentHelper.openInstaller(
compatActivity,
target.absolutePath,
compatActivity.getString(
compatActivity, target.absolutePath, compatActivity.getString(
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
)
} else if (response == IntentHelper.RESPONSE_URL) {
IntentHelper.openInstaller(
compatActivity,
uri.toString(),
compatActivity.getString(
compatActivity, uri.toString(), compatActivity.getString(
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
)
}

@ -41,7 +41,6 @@ import org.apache.commons.io.FileUtils
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.sql.Timestamp
import java.util.Objects
class SetupActivity : AppCompatActivity(), LanguageActivity {
@ -69,27 +68,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
}
val binding = ActivitySetupBinding.inflate(layoutInflater)
setContentView(binding.root)
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000)
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()
}
}
MainApplication.getInstance().check(this)
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
val ourPackageName = BuildConfig.APPLICATION_ID
@ -112,7 +91,12 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
if (packageName == foxPkgNameDebug || packageName == foxPkgNameFdroid || packageName == foxPkgNamePlay) {
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this)
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 ->
// start uninstall intent
val intent = Intent(Intent.ACTION_DELETE)
@ -122,11 +106,17 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
materialAlertDialogBuilder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
}
}
androidacyPkgName -> {
if (packageName == foxPkgName || packageName == foxPkgNameFdroid || packageName == foxPkgNameDebug || packageName == foxPkgNamePlay) {
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this)
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 ->
// start uninstall intent
val intent = Intent(Intent.ACTION_DELETE)
@ -136,11 +126,17 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
materialAlertDialogBuilder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
}
}
else -> {
if (packageName == foxPkgNameDebug) {
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this)
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 ->
// start uninstall intent
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 analyticsEnabled = view.findViewById<MaterialSwitch>(R.id.setup_app_analytics)
val crashReportingPii = view.findViewById<MaterialSwitch>(R.id.setup_crash_reporting_pii)
setupCrashReporting.isChecked =
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
setupCrashReporting.isChecked = BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
// if analytics is disabled, force disable crash reporting
if (!view.findViewById<MaterialSwitch>(R.id.setup_app_analytics).isChecked) {
setupCrashReporting.isEnabled = false
@ -167,20 +162,20 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
crashReportingPii.isChecked = false
}
// 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
analyticsEnabled.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (BuildConfig.DEBUG) Timber.i(
"Analytics: %s",
isChecked)
"Analytics: %s", isChecked
)
// if analytics is disabled, force disable crash reporting
if (!isChecked) {
setupCrashReporting.isChecked = false
setupCrashReporting.isEnabled = false
} else {
setupCrashReporting.isEnabled = true
setupCrashReporting.isChecked =
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
setupCrashReporting.isChecked = BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
}
if (!isChecked) {
setupAppAnalyticsSummary.setText(R.string.analytics_disabled_desc)
@ -189,8 +184,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
}
}
// pref_analytics_enabled
analyticsEnabled.isChecked =
BuildConfig.DEFAULT_ENABLE_ANALYTICS
analyticsEnabled.isChecked = BuildConfig.DEFAULT_ENABLE_ANALYTICS
// Repos are a little harder, as the enabled_repos build config is an arraylist
val andRepoView =
Objects.requireNonNull<Any>(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch
@ -202,32 +196,27 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
if (BuildConfig.DEBUG) {
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_background_update_check)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i(
"Automatic update Check: %s",
isChecked
"Automatic update Check: %s", isChecked
)
}
setupCrashReporting.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i(
"Crash Reporting: %s",
isChecked
"Crash Reporting: %s", isChecked
)
}
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_crash_reporting_pii)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i(
"Crash Reporting PII: %s",
isChecked
"Crash Reporting PII: %s", isChecked
)
}
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i(
"Androidacy Repo: %s",
isChecked
"Androidacy Repo: %s", isChecked
)
}
(Objects.requireNonNull<Any>(view.findViewById(R.id.setup_magisk_alt_repo)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
if (MainApplication.forceDebugLogging) Timber.i(
"Magisk Alt Repo: %s",
isChecked
"Magisk Alt Repo: %s", isChecked
)
}
}
@ -258,8 +247,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
builder.setCancelable(true)
// Create the dialog
builder.setSingleChoiceItems(
themeNames,
checkedItem
themeNames, checkedItem
) { dialog: DialogInterface, which: Int ->
// Set the theme
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.isChecked) {
Toast.makeText(
this,
R.string.setup_agree_eula_toast,
Toast.LENGTH_LONG
this, R.string.setup_agree_eula_toast, Toast.LENGTH_LONG
).show()
return@setOnClickListener
}
@ -329,8 +315,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
)
// Set the crash reporting pref
editor.putBoolean(
"pref_crash_reporting",
setupCrashReporting.isChecked
"pref_crash_reporting", setupCrashReporting.isChecked
)
// Set the crash reporting PII pref
editor.putBoolean(
@ -355,8 +340,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
if (MainApplication.forceDebugLogging) Timber.d("Saving preferences")
// now basically do the same thing for room db
val db = Room.databaseBuilder(
applicationContext,
ReposListDatabase::class.java, "ReposList.db"
applicationContext, ReposListDatabase::class.java, "ReposList.db"
).allowMainThreadQueries().build()
val androidacyRepoRoom = andRepoView.isChecked
val magiskAltRepoRoom = magiskAltRepoView.isChecked
@ -371,18 +355,27 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// Commit the changes
editor.commit()
// Log the changes
if (MainApplication.forceDebugLogging) Timber.d("Setup finished. Preferences: %s", prefs.all)
if (MainApplication.forceDebugLogging) Timber.d("Androidacy repo: %s", androidacyRepoRoom)
if (MainApplication.forceDebugLogging) Timber.d("Magisk Alt repo: %s", magiskAltRepoRoom)
if (MainApplication.forceDebugLogging) Timber.d(
"Setup finished. Preferences: %s",
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
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
MainActivity.doSetupRestarting = true
val pendingIntent = PendingIntent.getActivity(
this,
0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_IMMUTABLE
this, 0, Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE
)
try {
pendingIntent.send()
@ -459,17 +452,13 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
val thread = Thread {
if (MainApplication.forceDebugLogging) Timber.d("Creating databases")
val startTime = System.currentTimeMillis()
val appContext = MainApplication.INSTANCE!!.applicationContext
val appContext = MainApplication.getInstance().applicationContext
val db = Room.databaseBuilder(appContext, ReposListDatabase::class.java, "ReposList.db")
.fallbackToDestructiveMigration().build()
// same for modulelistcache
val db2 = Room.databaseBuilder(
appContext,
ModuleListCacheDatabase::class.java,
"ModuleListCache.db"
)
.fallbackToDestructiveMigration()
.allowMainThreadQueries().build()
appContext, ModuleListCacheDatabase::class.java, "ModuleListCache.db"
).fallbackToDestructiveMigration().allowMainThreadQueries().build()
val reposListDao = db.reposListDao()
val moduleListCacheDao = db2.moduleListCacheDao()
@ -533,9 +522,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// show a toast
runOnUiThread {
Toast.makeText(
this,
R.string.error_creating_repos_database,
Toast.LENGTH_LONG
this, R.string.error_creating_repos_database, Toast.LENGTH_LONG
).show()
}
} else {
@ -547,9 +534,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// show a toast
runOnUiThread {
Toast.makeText(
this,
R.string.error_creating_modulelistcache_database,
Toast.LENGTH_LONG
this, R.string.error_creating_modulelistcache_database, Toast.LENGTH_LONG
).show()
}
} else {
@ -558,7 +543,10 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// close the databases
db.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()
}
@ -572,17 +560,15 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// show a toast
runOnUiThread {
Toast.makeText(
this,
R.string.error_creating_cookie_database,
Toast.LENGTH_LONG
this, R.string.error_creating_cookie_database, Toast.LENGTH_LONG
).show()
}
}
// we literally only use these to create the http cache folders
try {
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/cronet"))
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.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/cronet"))
FileUtils.forceMkdir(File(MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm"))
FileUtils.forceMkdir(File(MainApplication.getInstance().dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js"))
} catch (e: IOException) {
Timber.e(e)
}

@ -12,7 +12,6 @@ import android.view.View
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.FileProvider
@ -30,7 +29,6 @@ import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.sql.Timestamp
import java.util.Objects
class UpdateActivity : AppCompatActivity() {
@ -41,27 +39,7 @@ class UpdateActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_update)
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000)
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()
}
}
MainApplication.getInstance().check(this)
chgWv = findViewById(R.id.changelog_webview)
val changelogWebView = chgWv!!
val webSettings = changelogWebView.settings
@ -80,18 +58,18 @@ class UpdateActivity : AppCompatActivity() {
WebView.setWebContentsDebuggingEnabled(true)
}
// 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
if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
WebSettingsCompat.setAlgorithmicDarkeningAllowed(webSettings, true)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
@Suppress("DEPRECATION")
WebSettingsCompat.setForceDark(webSettings, WebSettingsCompat.FORCE_DARK_ON)
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
@Suppress("DEPRECATION")
WebSettingsCompat.setForceDarkStrategy(
@Suppress("DEPRECATION") WebSettingsCompat.setForceDark(
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 -> {
// ensure path was passed and points to a file within our cache directory. replace .. and url encoded characters
val path =
intent.getStringExtra("path")?.trim { it <= ' ' }
?.replace("\\.\\.".toRegex(), "")?.replace("%2e%2e".toRegex(), "")
val path = intent.getStringExtra("path")?.trim { it <= ' ' }
?.replace("\\.\\.".toRegex(), "")?.replace("%2e%2e".toRegex(), "")
if (path!!.isEmpty()) {
runOnUiThread {
// set status text to error
@ -273,7 +250,8 @@ class UpdateActivity : AppCompatActivity() {
// set button text to download
val button = findViewById<MaterialButton>(R.id.action_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.visibility = View.VISIBLE
button.setOnClickListener {
@ -341,8 +319,7 @@ class UpdateActivity : AppCompatActivity() {
runOnUiThread {
// update progress bar
progressIndicator.setProgressCompat(
(downloaded.toFloat() / total.toFloat() * 100).toInt(),
true
(downloaded.toFloat() / total.toFloat() * 100).toInt(), true
)
// update status text
statusTextView.text = getString(
@ -422,9 +399,7 @@ class UpdateActivity : AppCompatActivity() {
val intent = Intent(Intent.ACTION_VIEW)
val context = applicationContext
val uri = FileProvider.getUriForFile(
context,
"${context.packageName}.file-provider",
updateFile!!
context, "${context.packageName}.file-provider", updateFile!!
)
intent.setDataAndTypeAndNormalize(uri, "application/vnd.android.package-archive")
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION

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

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

@ -236,7 +236,7 @@ class AndroidacyWebAPI(
*/
@get:JavascriptInterface
val isLightTheme: Boolean
get() = MainApplication.INSTANCE!!.isLightTheme
get() = MainApplication.getInstance().isLightTheme
/**
* 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.setContentText(context.getString(R.string.notification_channel_background_update_app_description))
if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
@ -120,7 +120,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
) {
return
}
if (MainApplication.INSTANCE!!.isInForeground) {
if (MainApplication.getInstance().isInForeground) {
// don't check if app is in foreground, this is a background check
return
}
@ -144,7 +144,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
// post checking notification if notifications are enabled
if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
@ -309,7 +309,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
}
// remove checking notification
if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
@ -328,7 +328,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
Timber.e(e, "Failed to check for updates")
// post notification
if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == 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
builder.setStyle(NotificationCompat.BigTextStyle().bigText(summary))
if (ContextCompat.checkSelfPermission(
MainApplication.INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return
}
// 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())
}

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

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

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

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

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

@ -8,7 +8,6 @@ import android.content.pm.PackageManager
import android.view.View
import androidx.annotation.StringRes
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.getPreferences
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!!
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?
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
try {
if (getPreferences("mmm")?.getStringSet(
"pref_background_update_check_excludes",
HashSet()
)!!
.contains(
"pref_background_update_check_excludes", HashSet()
)!!.contains(
moduleInfo!!.id
)
) 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"
// 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(
"pref_background_update_check_excludes_version",
HashSet()
"pref_background_update_check_excludes_version", HashSet()
)
var version = ""
if (MainApplication.forceDebugLogging) Timber.d(stringSetT.toString())
@ -185,19 +182,22 @@ class ModuleHolder : Comparable<ModuleHolder?> {
}
}
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
} else {
if (hasUpdate()) {
INSTANCE!!.modulesHaveUpdates = true
if (!INSTANCE!!.updateModules.contains(moduleId)) {
INSTANCE!!.updateModules += moduleId
INSTANCE!!.updateModuleCount++
MainApplication.getInstance().modulesHaveUpdates = true
if (!MainApplication.getInstance().updateModules.contains(moduleId)) {
MainApplication.getInstance().updateModules += moduleId
MainApplication.getInstance().updateModuleCount++
}
if (MainApplication.forceDebugLogging) Timber.d(
"modulesHaveUpdates = %s, updateModuleCount = %s",
INSTANCE!!.modulesHaveUpdates,
INSTANCE!!.updateModuleCount
MainApplication.getInstance().modulesHaveUpdates,
MainApplication.getInstance().updateModuleCount
)
Type.UPDATABLE
} else {
@ -209,18 +209,21 @@ class ModuleHolder : Comparable<ModuleHolder?> {
}
fun getCompareType(type: Type?): Type? {
return separator
?: if (notificationType != null && notificationType.special) {
Type.SPECIAL_NOTIFICATIONS
} else {
type
}
return separator ?: if (notificationType != null && notificationType.special) {
Type.SPECIAL_NOTIFICATIONS
} else {
type
}
}
fun shouldRemove(): Boolean {
// if type is not installable or updatable and we have repoModule, we should remove
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
}
// 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 === 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
}
// if type is installed but repomodule is not null, we should remove
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
}
// if lowqualitymodulefilter is enabled and module is low quality, remove
@ -255,9 +266,7 @@ class ModuleHolder : Comparable<ModuleHolder?> {
}
fun getButtons(
context: Context?,
buttonTypeList: MutableList<ActionButtonType?>,
showcaseMode: Boolean
context: Context?, buttonTypeList: MutableList<ActionButtonType?>, showcaseMode: Boolean
) {
if (!isModuleHolder) return
val localModuleInfo = moduleInfo
@ -281,16 +290,25 @@ class ModuleHolder : Comparable<ModuleHolder?> {
// set updatezipurl on moduleholder
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
}
if (repoModule != null) {
if (MainApplication.forceDebugLogging) Timber.d("repoModule: %s", repoModule!!.zipUrl)
if (MainApplication.forceDebugLogging) Timber.d(
"repoModule: %s",
repoModule!!.zipUrl
)
updateZipUrl = repoModule!!.zipUrl
}
// last ditch effort, try to get remoteModuleInfo from localModuleInfo
if (rInfo != null) {
if (MainApplication.forceDebugLogging) Timber.d("remoteModuleInfo: %s", rInfo.zipUrl)
if (MainApplication.forceDebugLogging) Timber.d(
"remoteModuleInfo: %s",
rInfo.zipUrl
)
updateZipUrl = rInfo.zipUrl
moduleInfo?.updateZipUrl = rInfo.zipUrl
}
@ -330,13 +348,23 @@ class ModuleHolder : Comparable<ModuleHolder?> {
Timber.w("Module %s has no moduleInfo", moduleId)
return false
}
if (repoModule == null && !INSTANCE!!.repoModules.containsKey(moduleId)) {
if (repoModule == null && !MainApplication.getInstance().repoModules.containsKey(moduleId)) {
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
}
} 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
}
Timber.d("Module %s has no update", moduleId)
@ -351,8 +379,7 @@ class ModuleHolder : Comparable<ModuleHolder?> {
val otherType = other.getCompareType(otherTypeReal)
val compare = selfType!!.compareTo(otherType!!)
return if (compare != 0) compare else if (selfTypeReal === otherTypeReal) selfTypeReal.compare(
this,
other
this, other
) else selfTypeReal.compareTo(otherTypeReal)
}
@ -454,9 +481,7 @@ class ModuleHolder : Comparable<ModuleHolder?> {
}
},
INSTALLABLE(
R.string.online_repo,
true,
true
R.string.online_repo, true, true
) {
override fun compare(o1: ModuleHolder?, o2: ModuleHolder?): Int {
if (o1 != null && o2 != null) {

@ -5,7 +5,6 @@ package com.fox2code.mmm.repo
import androidx.room.Room
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.MainApplication.Companion.getPreferences
import com.fox2code.mmm.utils.io.Hashes.Companion.hashSha256
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))
// now the same as above but for room database
val applicationContext = INSTANCE!!.applicationContext
val applicationContext = MainApplication.getInstance().applicationContext
val db = Room.databaseBuilder(
applicationContext, ReposListDatabase::class.java, "ReposList.db"
).allowMainThreadQueries().build()

@ -9,7 +9,6 @@ import com.fox2code.mmm.AppUpdateManager.Companion.shouldForceHide
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
import com.fox2code.mmm.R
import com.fox2code.mmm.XRepo
import com.fox2code.mmm.manager.ModuleInfo
@ -124,7 +123,7 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
isForceHide = shouldForceHide(tempVarForPreferenceId)
// basically same as above but for room database
val db = Room.databaseBuilder(
INSTANCE!!.applicationContext, ReposListDatabase::class.java, "ReposList.db"
MainApplication.getInstance().applicationContext, ReposListDatabase::class.java, "ReposList.db"
).allowMainThreadQueries().build()
val reposListRoom = db.reposListDao()
val reposListRoomList = reposListRoom.getById(preferenceId!!)
@ -278,7 +277,7 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
field
} else {
val db = Room.databaseBuilder(
INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java,
"ReposList.db",
).allowMainThreadQueries().build()
@ -295,7 +294,7 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
field = value
this.enabled = enabled && !isForceHide
val db = Room.databaseBuilder(
INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java,
"ReposList.db",
).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.
isForceHide = shouldForceHide(preferenceId!!)
val db = Room.databaseBuilder(
INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java,
"ReposList.db",
).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 (hasConnectivity()) {
@ -246,7 +246,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
if (!isLastUpdateSuccess || modules.isEmpty()) {
Timber.e("Failed to update %s", repoUpdaters[i]!!.repoData.name)
// 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 {
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
@ -299,7 +299,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
}
fun hasConnectivity(): Boolean {
return hasConnectivity(MainApplication.INSTANCE!!.applicationContext)
return hasConnectivity(MainApplication.getInstance().applicationContext)
}
private fun addRepoData(url: String, fallBackName: String?): RepoData {
@ -366,7 +366,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
if (INSTANCE == null || !INSTANCE!!.initialized) {
synchronized(lock) {
if (INSTANCE == null) {
val mainApplication = MainApplication.INSTANCE
val mainApplication = MainApplication.getInstance()
if (mainApplication != null) {
INSTANCE = RepoManager(mainApplication)
onRepoManagerInitialized()
@ -384,7 +384,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
if (INSTANCE == null) {
synchronized(lock) {
if (INSTANCE == null) {
val mainApplication = MainApplication.INSTANCE
val mainApplication = MainApplication.getInstance()
if (mainApplication != null) {
INSTANCE = RepoManager(mainApplication)
onRepoManagerInitialized()

@ -70,7 +70,7 @@ class RepoModule {
moduleInfo.flags = moduleInfo.flags or ModuleInfo.FLAG_METADATA_INVALID and ModuleInfo.FLAG_MM_REMOTE_MODULE
safe = moduleInfo.safe
// 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
}
}

@ -35,16 +35,16 @@ class RepoUpdater(repoData2: RepoData) {
return 0
}
// 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)
// convert to list for toUpdate
val toUpdateList = ArrayList<RepoModule>()
for (module in MainApplication.INSTANCE!!.repoModules) {
for (module in MainApplication.getInstance().repoModules) {
toUpdateList.add(module.value)
}
toUpdate = toUpdateList
// 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
}*/
// 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)
// now the above but for room
val db = Room.databaseBuilder(
MainApplication.INSTANCE!!,
MainApplication.getInstance(),
ModuleListCacheDatabase::class.java,
"ModuleListCache.db"
).allowMainThreadQueries().build()
@ -137,8 +137,8 @@ class RepoUpdater(repoData2: RepoData) {
toUpdate = repoData.populate(JSONObject(String(indexRaw!!, StandardCharsets.UTF_8)))
// Since we reuse instances this should work
toApply = HashSet(repoData.moduleHashMap.values)
// add toApply to the hashmap MainApplication.INSTANCE!!.repoModules
MainApplication.INSTANCE!!.repoModules.putAll(repoData.moduleHashMap)
// add toApply to the hashmap MainApplication.getInstance().repoModules
MainApplication.getInstance().repoModules.putAll(repoData.moduleHashMap)
(toUpdate as MutableList<RepoModule>?)?.let {
(toApply as HashSet<RepoModule>).removeAll(
it.toSet()
@ -174,7 +174,7 @@ class RepoUpdater(repoData2: RepoData) {
if (indexRaw != null) {
// set lastUpdate
val db = Room.databaseBuilder(
MainApplication.INSTANCE!!.applicationContext,
MainApplication.getInstance().applicationContext,
ReposListDatabase::class.java,
"ReposList.db"
).allowMainThreadQueries().build()

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

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

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

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

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

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

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

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

@ -81,7 +81,7 @@ enum class IntentHelper {;
} catch (e: ActivityNotFoundException) {
if (MainApplication.forceDebugLogging) Timber.d(e, "Could not find suitable activity to handle url")
Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString(
context, MainApplication.getInstance().lastActivity!!.getString(
R.string.no_browser
), Toast.LENGTH_LONG
).show()
@ -100,7 +100,7 @@ enum class IntentHelper {;
} catch (e: ActivityNotFoundException) {
if (MainApplication.forceDebugLogging) Timber.d(e, "Could not find suitable activity to handle url")
Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString(
context, MainApplication.getInstance().lastActivity!!.getString(
R.string.no_browser
), Toast.LENGTH_LONG
).show()
@ -113,15 +113,15 @@ enum class IntentHelper {;
val request = DownloadManager.Request(Uri.parse(url))
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
.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)
.setAllowedOverMetered(true)
.setAllowedOverRoaming(false)
.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)
Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString(
context, MainApplication.getInstance().lastActivity!!.getString(
R.string.download_started
), Toast.LENGTH_LONG
).show()
@ -136,7 +136,7 @@ enum class IntentHelper {;
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_FAILED) {
downloading = false
Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString(
context, MainApplication.getInstance().lastActivity!!.getString(
R.string.download_failed
), Toast.LENGTH_LONG
).show()
@ -144,7 +144,7 @@ enum class IntentHelper {;
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
downloading = false
Toast.makeText(
context, MainApplication.INSTANCE!!.lastActivity!!.getString(
context, MainApplication.getInstance().lastActivity!!.getString(
R.string.download_finished
), Toast.LENGTH_LONG
).show()
@ -357,7 +357,7 @@ enum class IntentHelper {;
intent1.putExtra(EXTRA_TAB_TOOLBAR_COLOR, typedValue.data)
intent1.putExtra(
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
) {
if (MainApplication.forceDebugLogging) Timber.i("Request Notification Permission")
if (MainApplication.INSTANCE!!.lastActivity!!
if (MainApplication.getInstance().lastActivity!!
.shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
) {
// Show a dialog explaining why we need context permission, which is to show

@ -20,7 +20,7 @@ class FileUtils {
try {
FileUtils.forceMkdir(
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(),
"/"
)
@ -28,7 +28,7 @@ class FileUtils {
)
FileUtils.forceMkdir(
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(),
"/"
)
@ -36,7 +36,7 @@ class FileUtils {
)
FileUtils.forceMkdir(
File(
(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/cronet").replace(
(MainApplication.getInstance().dataDir.toString() + "/cache/cronet").replace(
"//".toRegex(),
"/"
)

@ -207,7 +207,7 @@ enum class Files {
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
try {
val tempDir = File(MainApplication.INSTANCE!!.cacheDir, "temp")
val tempDir = File(MainApplication.getInstance().cacheDir, "temp")
if (tempDir.exists()) {
FileUtils.deleteDirectory(tempDir)
}

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

Loading…
Cancel
Save