diff --git a/README.md b/README.md index d9f4f5b..c17aaf0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b92f45e..2d38023 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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" } diff --git a/app/src/main/kotlin/com/fox2code/mmm/AppUpdateManager.kt b/app/src/main/kotlin/com/fox2code/mmm/AppUpdateManager.kt index e907cb5..b01e0e7 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/AppUpdateManager.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/AppUpdateManager.kt @@ -19,7 +19,7 @@ class AppUpdateManager private constructor() { private var changes: String? = null private val compatDataId = HashMap() 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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt b/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt index d742675..b0c7b56 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt @@ -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() diff --git a/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt index 1f3e4a2..cf5807f 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt @@ -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?, 1 + "enabled_repos", repoMap as Map?, 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(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) } } } diff --git a/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt b/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt index d3292d5..3d9a2ae 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt @@ -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() @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 } diff --git a/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt b/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt index c848d7a..66170a1 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt @@ -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 ) } diff --git a/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt index dfd6f4e..69412ad 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt @@ -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(R.id.setup_crash_reporting) val analyticsEnabled = view.findViewById(R.id.setup_app_analytics) val crashReportingPii = view.findViewById(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(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(R.id.setup_app_analytics_summary) + val setupAppAnalyticsSummary = + view.findViewById(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(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch @@ -202,32 +196,27 @@ class SetupActivity : AppCompatActivity(), LanguageActivity { if (BuildConfig.DEBUG) { (Objects.requireNonNull(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(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(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(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) } diff --git a/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt index 1df29c4..9830b42 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt @@ -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(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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyActivity.kt index c49d5b3..3ce4176 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyActivity.kt @@ -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( "https://production-api.androidacy.com/magisk/file//", @@ -387,6 +367,9 @@ class AndroidacyActivity : AppCompatActivity() { val headers = HashMap() 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() { diff --git a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyRepoData.kt b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyRepoData.kt index 1a7f7a7..d5eb960 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyRepoData.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyRepoData.kt @@ -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() diff --git a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyWebAPI.kt b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyWebAPI.kt index b531ce4..f80ad04 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyWebAPI.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyWebAPI.kt @@ -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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/background/BackgroundUpdateChecker.kt b/app/src/main/kotlin/com/fox2code/mmm/background/BackgroundUpdateChecker.kt index 14b0d5f..b326caf 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/background/BackgroundUpdateChecker.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/background/BackgroundUpdateChecker.kt @@ -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()) } diff --git a/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerActivity.kt index 9b30de9..7b288dc 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerActivity.kt @@ -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(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 = 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" -> { diff --git a/app/src/main/kotlin/com/fox2code/mmm/manager/LocalModuleInfo.kt b/app/src/main/kotlin/com/fox2code/mmm/manager/LocalModuleInfo.kt index 9c0414c..b8bf51c 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/manager/LocalModuleInfo.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/manager/LocalModuleInfo.kt @@ -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 } diff --git a/app/src/main/kotlin/com/fox2code/mmm/manager/ModuleManager.kt b/app/src/main/kotlin/com/fox2code/mmm/manager/ModuleManager.kt index c2be80a..aa5e42c 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/manager/ModuleManager.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/manager/ModuleManager.kt @@ -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 { diff --git a/app/src/main/kotlin/com/fox2code/mmm/markdown/MarkdownActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/markdown/MarkdownActivity.kt index 191e868..be5352f 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/markdown/MarkdownActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/markdown/MarkdownActivity.kt @@ -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) { diff --git a/app/src/main/kotlin/com/fox2code/mmm/module/ActionButtonType.kt b/app/src/main/kotlin/com/fox2code/mmm/module/ActionButtonType.kt index 667b774..4a2e4c3 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/module/ActionButtonType.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/module/ActionButtonType.kt @@ -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) { diff --git a/app/src/main/kotlin/com/fox2code/mmm/module/ModuleHolder.kt b/app/src/main/kotlin/com/fox2code/mmm/module/ModuleHolder.kt index 74f0343..366bc40 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/module/ModuleHolder.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/module/ModuleHolder.kt @@ -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 { 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 { 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 { // 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 { } } 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 { } 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 { } // 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 { } fun getButtons( - context: Context?, - buttonTypeList: MutableList, - showcaseMode: Boolean + context: Context?, buttonTypeList: MutableList, showcaseMode: Boolean ) { if (!isModuleHolder) return val localModuleInfo = moduleInfo @@ -281,16 +290,25 @@ class ModuleHolder : Comparable { // 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 { 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 { 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 { } }, 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) { diff --git a/app/src/main/kotlin/com/fox2code/mmm/repo/CustomRepoManager.kt b/app/src/main/kotlin/com/fox2code/mmm/repo/CustomRepoManager.kt index 0c5817d..ccb1c9b 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/repo/CustomRepoManager.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/repo/CustomRepoManager.kt @@ -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() diff --git a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoData.kt b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoData.kt index f300c28..f44d0e6 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoData.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoData.kt @@ -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() diff --git a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoManager.kt b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoManager.kt index 8ced67e..f4d5140 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoManager.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoManager.kt @@ -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() diff --git a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoModule.kt b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoModule.kt index b368cd7..869289e 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoModule.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoModule.kt @@ -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 } } diff --git a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoUpdater.kt b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoUpdater.kt index a2e08cc..bd85544 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoUpdater.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoUpdater.kt @@ -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() - 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?)?.let { (toApply as HashSet).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() diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/AppearanceFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/AppearanceFragment.kt index 300551d..e6c850f 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/AppearanceFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/AppearanceFragment.kt @@ -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("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("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 } diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/CreditsFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/CreditsFragment.kt index f9f258f..a7c911e 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/CreditsFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/CreditsFragment.kt @@ -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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/DebugFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/DebugFragment.kt index 83407e3..de9aaac 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/DebugFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/DebugFragment.kt @@ -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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/InfoFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/InfoFragment.kt index 83ee3c2..ea77f18 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/InfoFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/InfoFragment.kt @@ -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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/PrivacyFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/PrivacyFragment.kt index 5a6120d..7eaa222 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/PrivacyFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/PrivacyFragment.kt @@ -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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/RepoFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/RepoFragment.kt index a9a3c1a..3d7f4d3 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/RepoFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/RepoFragment.kt @@ -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(preferenceName + "_website")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { IntentHelper.openUrl( - MainApplication.INSTANCE!!.lastActivity!!, + MainApplication.getInstance().lastActivity!!, repoData.website ) true @@ -631,7 +631,7 @@ class RepoFragment : PreferenceFragmentCompat() { findPreference(preferenceName + "_support")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { IntentHelper.openUrl( - MainApplication.INSTANCE!!.lastActivity!!, + MainApplication.getInstance().lastActivity!!, repoData.support ) true @@ -644,7 +644,7 @@ class RepoFragment : PreferenceFragmentCompat() { findPreference(preferenceName + "_submit")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { IntentHelper.openUrl( - MainApplication.INSTANCE!!.lastActivity!!, + MainApplication.getInstance().lastActivity!!, repoData.submitModule ) true @@ -657,7 +657,7 @@ class RepoFragment : PreferenceFragmentCompat() { findPreference(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 = diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/SecurityFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/SecurityFragment.kt index ed6dfbe..2314a21 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/SecurityFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/SecurityFragment.kt @@ -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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/SettingsActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/SettingsActivity.kt index a83f481..389b70f 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/SettingsActivity.kt @@ -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("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("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) { diff --git a/app/src/main/kotlin/com/fox2code/mmm/settings/UpdateFragment.kt b/app/src/main/kotlin/com/fox2code/mmm/settings/UpdateFragment.kt index 80e67eb..2cfccc0 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/settings/UpdateFragment.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/settings/UpdateFragment.kt @@ -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("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("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("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) diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/IntentHelper.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/IntentHelper.kt index 3b5c759..1230a66 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/utils/IntentHelper.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/utils/IntentHelper.kt @@ -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 ) } } diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/RuntimeUtils.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/RuntimeUtils.kt index cde755b..f9c1305 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/utils/RuntimeUtils.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/utils/RuntimeUtils.kt @@ -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 diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/io/FileUtils.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/io/FileUtils.kt index 5a5e8b9..b9c1fc8 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/utils/io/FileUtils.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/utils/io/FileUtils.kt @@ -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(), "/" ) diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/io/Files.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/io/Files.kt index 9642f6e..2e0b577 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/utils/io/Files.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/utils/io/Files.kt @@ -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) } diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/io/net/Http.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/io/net/Http.kt index f91bffe..2a6b33a 100644 --- a/app/src/main/kotlin/com/fox2code/mmm/utils/io/net/Http.kt +++ b/app/src/main/kotlin/com/fox2code/mmm/utils/io/net/Http.kt @@ -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 private val fallbackCache: HashMap> @@ -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 }