Skip to content

Commit f6da136

Browse files
refactor
Signed-off-by: androidacy-user <opensource@androidacy.com>
1 parent d64efa6 commit f6da136

21 files changed

Lines changed: 445 additions & 212 deletions

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ dependencies {
363363
implementation("androidx.webkit:webkit:1.8.0")
364364
implementation("com.google.android.material:material:1.9.0")
365365

366-
implementation("com.mikepenz:aboutlibraries:10.8.3")
366+
implementation("com.mikepenz:aboutlibraries:10.9.1")
367367

368368
// Utils
369369
implementation("androidx.work:work-runtime:2.8.1")
@@ -405,7 +405,7 @@ dependencies {
405405
implementation("androidx.security:security-crypto:1.1.0-alpha06")
406406

407407
// some utils
408-
implementation("commons-io:commons-io:2.13.0")
408+
implementation("commons-io:commons-io:2.14.0")
409409
implementation("org.apache.commons:commons-compress:1.24.0")
410410

411411
// analytics

app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.annotation.SuppressLint
88
import android.content.ClipData
99
import android.content.ClipboardManager
1010
import android.content.DialogInterface
11+
import android.content.Intent
1112
import android.os.Bundle
1213
import android.view.View
1314
import android.widget.Toast
@@ -32,20 +33,25 @@ class CrashHandler : AppCompatActivity() {
3233
val crashDetails = findViewById<MaterialTextView>(R.id.crash_details)
3334
crashDetails.text = ""
3435
// get the exception from the intent
35-
val exception = intent.getSerializableExtra("exception") as Throwable?
36-
// get the crashReportingEnabled from the intent
37-
intent.getBooleanExtra("crashReportingEnabled", false)
36+
val exceptionFromIntent = intent.getSerializableExtra("exception") as Throwable?
37+
var exception: String? = null
38+
// parse the exception from the intent into exception if it is not null
39+
if (exceptionFromIntent != null) {
40+
val stringWriter = StringWriter()
41+
exceptionFromIntent.printStackTrace(PrintWriter(stringWriter))
42+
var stacktrace = stringWriter.toString()
43+
stacktrace = stacktrace.replace(",", "\n ")
44+
exception = stacktrace
45+
}
46+
val sharedPreferences = MainApplication.getPreferences("mmm")
47+
if (exception == null && sharedPreferences != null) {
48+
exception = sharedPreferences.getString("pref_crash_stacktrace", null)
49+
}
3850
// if the exception is null, set the crash details to "Unknown"
3951
if (exception == null) {
4052
crashDetails.setText(R.string.crash_details)
4153
} else {
42-
// if the exception is not null, set the crash details to the exception and stacktrace
43-
// stacktrace is an StacktraceElement, so convert it to a string and replace the commas with newlines
44-
val stringWriter = StringWriter()
45-
exception.printStackTrace(PrintWriter(stringWriter))
46-
var stacktrace = stringWriter.toString()
47-
stacktrace = stacktrace.replace(",", "\n ")
48-
crashDetails.text = getString(R.string.crash_full_stacktrace, stacktrace)
54+
crashDetails.text = getString(R.string.crash_full_stacktrace, exception)
4955
}
5056
// handle reset button
5157
findViewById<View>(R.id.reset).setOnClickListener { _: View? ->
@@ -60,6 +66,16 @@ class CrashHandler : AppCompatActivity() {
6066
builder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
6167
builder.show()
6268
}
69+
// restart button
70+
findViewById<View>(R.id.restart).setOnClickListener { _: View? ->
71+
// restart the app
72+
val intent = packageManager.getLaunchIntentForPackage(packageName)
73+
intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
74+
startActivity(intent)
75+
finish()
76+
}
77+
// remove pref_crashed from shared preferences
78+
sharedPreferences?.edit()?.remove("pref_crashed")?.apply()
6379
}
6480

6581
fun copyCrashDetails(view: View) {

app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt

Lines changed: 122 additions & 63 deletions
Large diffs are not rendered by default.

app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,28 @@
44

55
package com.fox2code.mmm
66

7+
import android.Manifest
78
import android.annotation.SuppressLint
89
import android.app.Activity
910
import android.app.ActivityManager
1011
import android.app.ActivityManager.RunningAppProcessInfo
1112
import android.app.Application
1213
import android.app.Application.ActivityLifecycleCallbacks
14+
import android.app.PendingIntent
1315
import android.content.Context
1416
import android.content.Intent
1517
import android.content.SharedPreferences
1618
import android.content.pm.PackageManager
1719
import android.content.res.Resources
1820
import android.os.Build
1921
import android.os.Bundle
20-
import android.os.Process
2122
import android.os.SystemClock
2223
import android.util.Log
2324
import androidx.annotation.StyleRes
2425
import androidx.appcompat.app.AppCompatActivity
2526
import androidx.appcompat.view.ContextThemeWrapper
27+
import androidx.core.app.ActivityCompat
28+
import androidx.core.app.NotificationCompat
2629
import androidx.core.app.NotificationManagerCompat
2730
import androidx.emoji2.text.DefaultEmojiCompatConfig
2831
import androidx.emoji2.text.EmojiCompat
@@ -48,6 +51,8 @@ import ly.count.android.sdk.Countly
4851
import ly.count.android.sdk.CountlyConfig
4952
import timber.log.Timber
5053
import java.io.File
54+
import java.io.PrintWriter
55+
import java.io.StringWriter
5156
import java.security.SecureRandom
5257
import java.text.SimpleDateFormat
5358
import java.util.Date
@@ -202,23 +207,49 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
202207

203208
Thread.setDefaultUncaughtExceptionHandler { _: Thread?, throwable: Throwable ->
204209
clearCachedSharedPrefs()
205-
// open crash handler and exit
210+
// send high importance notification with pending intent to open CrashHandler activity with stacktrace
206211
val intent = Intent(this, CrashHandler::class.java)
207-
// pass the entire exception to the crash handler
208212
intent.putExtra("exception", throwable)
209-
// add stacktrace as string
210-
intent.putExtra("stacktrace", throwable.stackTrace)
211-
// serialize Sentry.captureException and pass it to the crash handler
212-
intent.putExtra("sentryException", throwable)
213-
// pass crashReportingEnabled to crash handler
214-
intent.putExtra("crashReportingEnabled", isCrashReportingEnabled)
215-
// add isCrashing to intent
216213
intent.putExtra("isCrashing", true)
217-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
218-
Timber.e("Starting crash handler")
219-
startActivity(intent)
220-
Timber.e("Exiting")
221-
Process.killProcess(Process.myPid())
214+
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
215+
val pendingIntent = PendingIntent.getActivity(
216+
this, 0, intent,
217+
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
218+
)
219+
// set pref
220+
val sharedPreferences = getPreferences("mmm")
221+
val editor = sharedPreferences!!.edit()
222+
editor.putBoolean("pref_crashed", true)
223+
val stringWriter = StringWriter()
224+
throwable.printStackTrace(PrintWriter(stringWriter))
225+
val stacktrace = stringWriter.toString()
226+
editor.putString("pref_crash_stacktrace", stacktrace)
227+
editor.apply()
228+
val crashreportingenabled = sharedPreferences.getBoolean(
229+
"pref_crashreportingenabled",
230+
true
231+
)
232+
// send notification
233+
val notificationManagerCompat = NotificationManagerCompat.from(this)
234+
val notifBody = if (crashreportingenabled) getString(R.string.crash_notification_body) else getString(
235+
R.string.crash_notification_body_noreport
236+
)
237+
val notification = NotificationCompat.Builder(this, "crash")
238+
.setSmallIcon(R.drawable.ic_baseline_error_24)
239+
.setContentTitle(getString(R.string.crash_notification_title))
240+
.setContentText(notifBody)
241+
.setPriority(NotificationCompat.PRIORITY_HIGH)
242+
.setCategory(NotificationCompat.CATEGORY_ERROR)
243+
.setContentIntent(pendingIntent)
244+
.setAutoCancel(true)
245+
.build()
246+
if (ActivityCompat.checkSelfPermission(
247+
this,
248+
Manifest.permission.POST_NOTIFICATIONS
249+
) == PackageManager.PERMISSION_GRANTED
250+
) {
251+
notificationManagerCompat.notify(0, notification)
252+
}
222253
}
223254
supportedLocales.addAll(
224255
listOf(
@@ -604,7 +635,6 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
604635
} catch (e: Exception) {
605636
// try again five times, with a 250ms delay between each try. if we still can't get the shared preferences, throw an exception
606637
var i = 0
607-
var s = false
608638
while (i < 5) {
609639
try {
610640
Thread.sleep(250)
@@ -623,7 +653,6 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
623653
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
624654
)
625655
mSharedPrefs!![name] = sharedPreferences
626-
s = true
627656
return sharedPreferences
628657
} catch (e: Exception) {
629658
Timber.e(e, "Failed to get shared preferences")
@@ -714,7 +743,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
714743
}
715744

716745
val isCrashReportingEnabled: Boolean
717-
get() = getPreferences("mmm")!!.getBoolean(
746+
get() = analyticsAllowed() && getPreferences("mmm")!!.getBoolean(
718747
"pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
719748
)
720749
val bootSharedPreferences: SharedPreferences?
@@ -736,6 +765,9 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
736765

737766
fun shouldShowFeedback(): Boolean {
738767
// should not have been shown in 14 days and only 1 in 5 chance
768+
if (!analyticsAllowed()) {
769+
return false
770+
}
739771
val randChance = Random().nextInt(5)
740772
val lastShown = getPreferences("mmm")!!.getLong("last_feedback", 0)
741773
if (forceDebugLogging) Timber.d(
@@ -745,6 +777,8 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
745777
)
746778
return System.currentTimeMillis() - lastShown > 1209600000 && randChance == 0
747779
}
780+
781+
var dirty = false
748782
}
749783

750784
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {

app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import com.google.android.material.button.MaterialButton
3535
import com.google.android.material.checkbox.MaterialCheckBox
3636
import com.google.android.material.dialog.MaterialAlertDialogBuilder
3737
import com.google.android.material.materialswitch.MaterialSwitch
38+
import com.google.android.material.textview.MaterialTextView
3839
import com.topjohnwu.superuser.internal.UiThreadHandler
3940
import org.apache.commons.io.FileUtils
4041
import timber.log.Timber
@@ -54,6 +55,9 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
5455
this.window.navigationBarColor = this.getColor(R.color.black_transparent)
5556
createFiles()
5657
disableUpdateActivityForFdroidFlavor()
58+
if (BuildConfig.DEBUG) {
59+
Timber.d("Starting SetupActivity")
60+
}
5761
// Set theme
5862
val prefs = MainApplication.getPreferences("mmm")!!
5963
when (prefs.getString("theme", "system")) {
@@ -155,37 +159,38 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
155159
val crashReportingPii = view.findViewById<MaterialSwitch>(R.id.setup_crash_reporting_pii)
156160
setupCrashReporting.isChecked =
157161
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
158-
// pref_crash_reporting_pii
159-
crashReportingPii.isChecked =
160-
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING_PII
161-
// pref_analytics_enabled
162-
analyticsEnabled.isChecked =
163-
BuildConfig.DEFAULT_ENABLE_ANALYTICS
164162
// if analytics is disabled, force disable crash reporting
165163
if (!view.findViewById<MaterialSwitch>(R.id.setup_app_analytics).isChecked) {
166164
setupCrashReporting.isEnabled = false
167165
crashReportingPii.isEnabled = false
168166
setupCrashReporting.isChecked = false
169167
crashReportingPii.isChecked = false
170168
}
169+
// switch summary for setup_app_analytics_summary
170+
val setupAppAnalyticsSummary = view.findViewById<MaterialTextView>(R.id.setup_app_analytics_summary)
171171
// listen for changes to the analytics switch
172172
analyticsEnabled.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
173+
if (BuildConfig.DEBUG) Timber.i(
174+
"Analytics: %s",
175+
isChecked)
173176
// if analytics is disabled, force disable crash reporting
174177
if (!isChecked) {
175178
setupCrashReporting.isChecked = false
176-
crashReportingPii.isChecked = false
177179
setupCrashReporting.isEnabled = false
178-
crashReportingPii.isEnabled = false
179180
} else {
180181
setupCrashReporting.isEnabled = true
181-
crashReportingPii.isEnabled = true
182+
setupCrashReporting.isChecked =
183+
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
184+
}
185+
if (!isChecked) {
186+
setupAppAnalyticsSummary.setText(R.string.analytics_disabled_desc)
187+
} else {
188+
setupAppAnalyticsSummary.setText(R.string.analytics_enabled_desc)
182189
}
183190
}
184-
// assert that both switches match the build config on debug builds
185-
if (BuildConfig.DEBUG) {
186-
assert((Objects.requireNonNull<Any>(view.findViewById(R.id.setup_background_update_check)) as MaterialSwitch).isChecked == BuildConfig.ENABLE_AUTO_UPDATER)
187-
assert(setupCrashReporting.isChecked == BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING)
188-
}
191+
// pref_analytics_enabled
192+
analyticsEnabled.isChecked =
193+
BuildConfig.DEFAULT_ENABLE_ANALYTICS
189194
// Repos are a little harder, as the enabled_repos build config is an arraylist
190195
val andRepoView =
191196
Objects.requireNonNull<Any>(view.findViewById(R.id.setup_androidacy_repo)) as MaterialSwitch
@@ -362,7 +367,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
362367
reposListDao.setEnabled(androidacyRepoRoomObj.id, androidacyRepoRoom)
363368
reposListDao.setEnabled(magiskAltRepoRoomObj.id, magiskAltRepoRoom)
364369
db.close()
365-
editor.putString("last_shown_setup", "v5")
370+
editor.putString("last_shown_setup", "v6")
366371
// Commit the changes
367372
editor.commit()
368373
// Log the changes
@@ -395,6 +400,8 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
395400
// close the app
396401
finish()
397402
}
403+
// log finish
404+
if (MainApplication.forceDebugLogging) Timber.d("SetupActivity finished oncreate")
398405
}
399406

400407
override fun getTheme(): Theme {

app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyRepoData.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,22 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
478478
OK_HTTP_URL_BUILDER.build()
479479
}
480480

481+
private var realInstance: AndroidacyRepoData? = null
482+
get() {
483+
if (field === null) {
484+
field = AndroidacyRepoData(INSTANCE!!.cacheDir, false)
485+
}
486+
return field
487+
}
488+
481489
val instance: AndroidacyRepoData
482-
get() = RepoManager.getINSTANCE()!!.androidacyRepoData!!
490+
get() {
491+
return if (RepoManager.getINSTANCE()!!.androidacyRepoData !== null) {
492+
RepoManager.getINSTANCE()!!.androidacyRepoData!!
493+
} else {
494+
realInstance!!
495+
}
496+
}
483497

484498
private fun filterURL(url: String?): String? {
485499
return if (url.isNullOrEmpty() || isInvalidURL(url)) {

app/src/main/kotlin/com/fox2code/mmm/background/BackgroundUpdateChecker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
378378
fun onMainActivityCreate(context: Context) {
379379
// Refuse to run if first_launch pref is not false
380380
if (MainApplication.getPreferences("mmm")!!
381-
.getString("last_shown_setup", null) != "v5"
381+
.getString("last_shown_setup", null) != "v6"
382382
) return
383383
// create notification channel group
384384
val groupName: CharSequence = context.getString(R.string.notification_group_updates)

app/src/main/kotlin/com/fox2code/mmm/installer/InstallerActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,8 @@ class InstallerActivity : AppCompatActivity() {
589589
if (suFile.exists() && !suFile.delete()) Timber.w("Failed to delete zip file") else toDelete =
590590
null
591591
} else toDelete = null
592+
// set dirty in mainapp
593+
MainApplication.dirty = true
592594
runOnUiThread {
593595
this.window.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 0)
594596
// release wakelock

app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,13 @@ class InstallerInitializer {
7575
}
7676

7777
fun tryGetMagiskPathAsync(callback: Callback, forceCheck: Boolean = false) {
78-
val mgskPth = mgskPth
7978
val thread: Thread = object : Thread("Magisk GetPath Thread") {
8079
override fun run() {
8180
if (mgskPth != null && !forceCheck) {
8281
callback.onPathReceived(mgskPth)
8382
return
8483
}
8584
var error: Int
86-
@Suppress("NAME_SHADOWING") var mgskPth: String? = null
8785
try {
8886
mgskPth = tryGetMagiskPath(forceCheck)
8987
error = ERROR_NO_PATH
@@ -95,12 +93,14 @@ class InstallerInitializer {
9593
Timber.e(e)
9694
}
9795
if (forceCheck) {
98-
Companion.mgskPth = mgskPth
9996
if (mgskPth == null) {
10097
mgskVerCode = 0
10198
}
10299
}
103100
if (mgskPth != null) {
101+
if (MainApplication.forceDebugLogging) {
102+
Timber.i("Magisk path async: %s", mgskPth)
103+
}
104104
MainApplication.setHasGottenRootAccess(true)
105105
callback.onPathReceived(mgskPth)
106106
} else {

0 commit comments

Comments
 (0)