diff --git a/.github/workflows/build-debug.yml b/.github/workflows/build-debug.yml
index 30d9279..b4ccfb1 100644
--- a/.github/workflows/build-debug.yml
+++ b/.github/workflows/build-debug.yml
@@ -62,33 +62,33 @@ jobs:
# UPLOAD ARTIFACT SECTION
# Will be shorter, when https://github.com/actions/upload-artifact/pull/354 will be merged
- # FoxMMM-default-debug
- - name: Upload FoxMMM-default-arm64-v8a-debug
+ # AMMM-default-debug
+ - name: Upload AMMM-default-arm64-v8a-debug
uses: actions/upload-artifact@v3
with:
- name: FoxMMM-default-arm64-v8a-debug
+ name: AMMM-default-arm64-v8a-debug
path: app/build/outputs/apk/default/debug/*-default-arm64-v8a-debug.apk
- - name: Upload FoxMMM-default-armeabi-v7a-debug
+ - name: Upload AMMM-default-armeabi-v7a-debug
uses: actions/upload-artifact@v3
with:
- name: FoxMMM-default-armeabi-v7a-debug
+ name: AMMM-default-armeabi-v7a-debug
path: app/build/outputs/apk/default/debug/*-default-armeabi-v7a-debug.apk
- - name: Upload FoxMMM-default-universal-debug
+ - name: Upload AMMM-default-universal-debug
uses: actions/upload-artifact@v3
with:
- name: FoxMMM-default-universal-debug
+ name: AMMM-default-universal-debug
path: app/build/outputs/apk/default/debug/*-default-universal-debug.apk
- - name: Upload FoxMMM-default-x86-debug
+ - name: Upload AMMM-default-x86-debug
uses: actions/upload-artifact@v3
with:
- name: FoxMMM-default-x86-debug
+ name: AMMM-default-x86-debug
path: app/build/outputs/apk/default/debug/*-default-x86-debug.apk
- - name: Upload FoxMMM-default-x86_64-debug
+ - name: Upload AMMM-default-x86_64-debug
uses: actions/upload-artifact@v3
with:
- name: FoxMMM-default-x86_64-debug
+ name: AMMM-default-x86_64-debug
path: app/build/outputs/apk/default/debug/*-default-x86_64-debug.apk
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 698c0bf..5b597c9 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -5,7 +5,6 @@
@file:Suppress("UnstableApiUsage", "SpellCheckingInspection")
import com.android.build.api.variant.FilterConfiguration.FilterType.ABI
-import io.sentry.android.gradle.instrumentation.logcat.LogcatLevel
import java.util.Properties
plugins {
@@ -14,11 +13,8 @@ plugins {
id("com.mikepenz.aboutlibraries.plugin")
kotlin("android")
kotlin("kapt")
- id("com.google.devtools.ksp") version "1.8.22-1.0.11"
- id("io.sentry.android.gradle") version "3.12.0"
+ id("com.google.devtools.ksp") version "1.9.10-1.0.13"
}
-
-val hasSentryConfig = File(rootProject.projectDir, "sentry.properties").exists()
android {
// functions to get git info: gitCommitHash, gitBranch, gitRemote
val gitCommitHash = providers.exec {
@@ -52,8 +48,8 @@ android {
applicationId = "com.fox2code.mmm"
minSdk = 26
targetSdk = 34
- versionCode = 85
- versionName = "2.3.1"
+ versionCode = 86
+ versionName = "2.3.2"
vectorDrawables {
useSupportLibrary = true
}
@@ -85,10 +81,6 @@ android {
"en"
)
)
- // ksp room processor
- room {
- schemaLocationDir.set(file("roomSchemas"))
- }
}
splits {
@@ -159,39 +151,23 @@ android {
buildConfigField("boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "true")
buildConfigField("boolean", "DEFAULT_ENABLE_CRASH_REPORTING_PII", "true")
buildConfigField("boolean", "DEFAULT_ENABLE_ANALYTICS", "true")
- val properties = Properties()
- if (project.rootProject.file("local.properties").exists()) {
- properties.load(project.rootProject.file("local.properties").reader())
- // grab matomo.url
- buildConfigField(
- "String", "ANALYTICS_ENDPOINT", "\"" + properties.getProperty(
- "matomo.url", "https://s-api.androidacy.com/matomo.php"
- ) + "\""
- )
- } else {
- buildConfigField(
- "String", "ANALYTICS_ENDPOINT", "\"https://s-api.androidacy.com/matomo.php\""
- )
- }
buildConfigField("boolean", "ENABLE_PROTECTION", "true")
// Get the androidacy client ID from the androidacy.properties
val propertiesA = Properties()
- // If androidacy.properties doesn"t exist, use the default client ID which is heavily
- // rate limited to 30 requests per minute
+ val default = "5KYccdYxWB2RxMq5FTbkWisXi2dS6yFN9R7RVlFCG98FRdz6Mf5ojY2fyJCUlXJZ"
if (project.rootProject.file("androidacy.properties").exists()) {
propertiesA.load(project.rootProject.file("androidacy.properties").reader())
- properties.setProperty(
- "client_id", "\"" + propertiesA.getProperty(
+ propertiesA.setProperty(
+ "client_id", propertiesA.getProperty(
"client_id",
- "5KYccdYxWB2RxMq5FTbkWisXi2dS6yFN9R7RVlFCG98FRdz6Mf5ojY2fyJCUlXJZ"
- ) + "\""
+ default
+ )
)
} else {
- properties.setProperty(
- "client_id", "5KYccdYxWB2RxMq5FTbkWisXi2dS6yFN9R7RVlFCG98FRdz6Mf5ojY2fyJCUlXJZ"
- )
+ propertiesA.setProperty("client_id", "\"" + default + "\"")
}
+
buildConfigField(
"String", "ANDROIDACY_CLIENT_ID", "\"" + propertiesA.getProperty("client_id") + "\""
)
@@ -228,38 +204,21 @@ android {
buildConfigField("boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "true")
buildConfigField("boolean", "DEFAULT_ENABLE_CRASH_REPORTING_PII", "true")
buildConfigField("boolean", "DEFAULT_ENABLE_ANALYTICS", "true")
- val properties = Properties()
- if (project.rootProject.file("local.properties").exists()) {
- properties.load(project.rootProject.file("local.properties").reader())
- // grab matomo.url
- buildConfigField(
- "String", "ANALYTICS_ENDPOINT", "\"" + properties.getProperty(
- "matomo.url", "https://s-api.androidacy.com/matomo.php"
- ) + "\""
- )
- } else {
- buildConfigField(
- "String", "ANALYTICS_ENDPOINT", "\"https://s-api.androidacy.com/matomo.php\""
- )
- }
buildConfigField("boolean", "ENABLE_PROTECTION", "true")
// Get the androidacy client ID from the androidacy.properties
val propertiesA = Properties()
- // If androidacy.properties doesn"t exist, use the default client ID which is heavily
- // rate limited to 30 requests per minute
+ val default = "5KYccdYxWB2RxMq5FTbkWisXi2dS6yFN9R7RVlFCG98FRdz6Mf5ojY2fyJCUlXJZ"
if (project.rootProject.file("androidacy.properties").exists()) {
propertiesA.load(project.rootProject.file("androidacy.properties").reader())
- properties.setProperty(
- "client_id", "\"" + propertiesA.getProperty(
- "play_client_id",
- "5KYccdYxWB2RxMq5FTbkWisXi2dS6yFN9R7RVlFCG98FRdz6Mf5ojY2fyJCUlXJZ"
- ) + "\""
+ propertiesA.setProperty(
+ "client_id", propertiesA.getProperty(
+ "client_id",
+ default
+ )
)
} else {
- properties.setProperty(
- "client_id", "5KYccdYxWB2RxMq5FTbkWisXi2dS6yFN9R7RVlFCG98FRdz6Mf5ojY2fyJCUlXJZ"
- )
+ propertiesA.setProperty("client_id", "\"" + default + "\"")
}
buildConfigField(
"String", "ANDROIDACY_CLIENT_ID", "\"" + propertiesA.getProperty("client_id") + "\""
@@ -300,20 +259,6 @@ android {
buildConfigField("boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "false")
buildConfigField("boolean", "DEFAULT_ENABLE_CRASH_REPORTING_PII", "false")
buildConfigField("boolean", "DEFAULT_ENABLE_ANALYTICS", "false")
- val properties = Properties()
- if (project.rootProject.file("local.properties").exists()) {
- properties.load(project.rootProject.file("local.properties").reader())
- // grab matomo.url
- buildConfigField(
- "String", "ANALYTICS_ENDPOINT", "\"" + properties.getProperty(
- "matomo.url", "https://s-api.androidacy.com/matomo.php"
- ) + "\""
- )
- } else {
- buildConfigField(
- "String", "ANALYTICS_ENDPOINT", "\"https://s-api.androidacy.com/matomo.php\""
- )
- }
buildConfigField("boolean", "ENABLE_PROTECTION", "true")
// Repo with ads or tracking feature are disabled by default for the
@@ -355,44 +300,6 @@ android {
}
}
-sentry {
-
- includeProguardMapping.set(true)
-
- autoUploadProguardMapping.set(hasSentryConfig)
-
- experimentalGuardsquareSupport.set(true)
-
- uploadNativeSymbols.set(hasSentryConfig)
-
- includeNativeSources.set(hasSentryConfig)
-
- tracingInstrumentation {
- enabled.set(true)
-
- logcat {
- enabled.set(true)
-
- minLevel.set(LogcatLevel.WARNING)
- }
- }
-
- autoInstallation {
- enabled.set(true)
- }
-
- includeDependenciesReport.set(true)
- includeSourceContext.set(hasSentryConfig)
-
- // Includes additional source directories into the source bundle.
- // These directories are resolved relative to the project directory.
- additionalSourceDirsForSourceContext.set(setOf("src/main/java", "src/main/kotlin"))
-
- org.set("androidacy")
- projectName.set("foxmmm")
- uploadNativeSymbols.set(hasSentryConfig)
-}
-
val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3, "arm64-v8a" to 4)
// For per-density APKs, create a similar map:
@@ -452,11 +359,7 @@ dependencies {
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.webkit:webkit:1.8.0")
implementation("com.google.android.material:material:1.9.0")
- implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0")
- implementation("dev.rikka.rikkax.insets:insets:1.3.0")
- implementation("com.github.KieronQuinn:MonetCompat:0.4.1")
- implementation("com.github.Fox2Code.FoxCompat:foxcompat:1.2.14")
- implementation("com.github.Fox2Code.FoxCompat:hiddenapis:1.2.14")
+
implementation("com.mikepenz:aboutlibraries:10.8.3")
// Utils
@@ -481,9 +384,6 @@ dependencies {
implementation("com.github.Fox2Code:RosettaX:1.0.9")
implementation("com.github.Fox2Code:AndroidANSI:1.2.1")
- // sentry
- implementation("io.sentry:sentry-android:6.29.0")
-
// Markdown
// TODO: switch to an updated implementation
implementation("io.noties.markwon:core:4.6.2")
@@ -493,10 +393,6 @@ dependencies {
implementation("com.google.net.cronet:cronet-okhttp:0.1.0")
implementation("com.caverock:androidsvg:1.4")
- implementation("dev.rikka.rikkax.core:core:1.4.1")
- implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
- implementation("com.github.tiann:FreeReflection:3.1.0")
-
implementation("androidx.core:core-ktx:1.12.0")
// timber
@@ -510,7 +406,7 @@ dependencies {
implementation("org.apache.commons:commons-compress:1.24.0")
// analytics
- implementation("com.github.matomo-org:matomo-sdk-android:HEAD")
+ implementation("ly.count.android:sdk:23.8.2")
// annotations
implementation("org.jetbrains:annotations-java5:24.0.1")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3927c50..d8a8e0e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,8 +4,6 @@
tools:ignore="QueryAllPackagesPermission"
tools:targetApi="tiramisu">
-
-
@@ -32,9 +30,12 @@
-
+
-
+
@@ -187,31 +188,6 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/shared_file_paths" />
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt b/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt
index 6a0a548..292b5c9 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/CrashHandler.kt
@@ -10,20 +10,16 @@ import android.content.ClipboardManager
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
-import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textview.MaterialTextView
-import io.sentry.Sentry
-import io.sentry.UserFeedback
-import io.sentry.protocol.SentryId
import timber.log.Timber
import java.io.PrintWriter
import java.io.StringWriter
class CrashHandler : AppCompatActivity() {
- @Suppress("DEPRECATION", "KotlinConstantConditions")
+ @Suppress("DEPRECATION")
@SuppressLint("RestrictedApi")
override fun onCreate(savedInstanceState: Bundle?) {
Timber.i("CrashHandler.onCreate(%s)", savedInstanceState)
@@ -38,7 +34,7 @@ class CrashHandler : AppCompatActivity() {
// get the exception from the intent
val exception = intent.getSerializableExtra("exception") as Throwable?
// get the crashReportingEnabled from the intent
- val crashReportingEnabled = intent.getBooleanExtra("crashReportingEnabled", false)
+ intent.getBooleanExtra("crashReportingEnabled", false)
// if the exception is null, set the crash details to "Unknown"
if (exception == null) {
crashDetails.setText(R.string.crash_details)
@@ -51,113 +47,6 @@ class CrashHandler : AppCompatActivity() {
stacktrace = stacktrace.replace(",", "\n ")
crashDetails.text = getString(R.string.crash_full_stacktrace, stacktrace)
}
- // force sentry to send all events
- Sentry.flush(2000)
- var lastEventId = intent.getStringExtra("lastEventId")
- // if event id is all zeros, set it to "". test this by matching the regex ^0+$ (all zeros)
- if (lastEventId?.matches("^0+$".toRegex()) == true) {
- lastEventId = ""
- }
- if (BuildConfig.DEBUG) Timber.d(
- "CrashHandler.onCreate: lastEventId=%s, crashReportingEnabled=%s",
- lastEventId,
- crashReportingEnabled
- )
-
- // get name, email, and message fields
- val name = findViewById(R.id.feedback_name)
- val email = findViewById(R.id.feedback_email)
- val description = findViewById(R.id.feedback_message)
- val submit = findViewById(R.id.feedback_submit)
- if (lastEventId.isNullOrEmpty() && crashReportingEnabled) {
- // if lastEventId is null, hide the feedback button
- if (BuildConfig.DEBUG) Timber.d("CrashHandler.onCreate: lastEventId is null but crash reporting is enabled. This may indicate a bug in the crash reporting system.")
- submit.visibility = View.GONE
- findViewById(R.id.feedback_text).setText(R.string.no_sentry_id)
- } else {
- // if lastEventId is not null, enable the feedback name, email, message, and submit button
- email.isEnabled = true
- name.isEnabled = true
- description.isEnabled = true
- submit.isEnabled = true
- }
- // disable feedback if sentry is disabled
- if (crashReportingEnabled && lastEventId != null) {
- // get submit button
- findViewById(R.id.feedback_submit).setOnClickListener { _: View? ->
- // require the feedback_message, rest is optional
- if (description.text.toString() == "") {
- Toast.makeText(this, R.string.sentry_dialogue_empty_message, Toast.LENGTH_LONG)
- .show()
- return@setOnClickListener
- }
- // if email or name is empty, use "Anonymous"
- val nameString =
- arrayOf(if (name.text.toString() == "") "Anonymous" else name.text.toString())
- val emailString =
- arrayOf(if (email.text.toString() == "") "Anonymous" else email.text.toString())
- Thread {
- try {
- val userFeedback =
- UserFeedback(SentryId(lastEventId))
- // Setups the JSON body
- if (nameString[0] == "") nameString[0] = "Anonymous"
- if (emailString[0] == "") emailString[0] = "Anonymous"
- userFeedback.name = nameString[0]
- userFeedback.email = emailString[0]
- userFeedback.comments = description.text.toString()
- Sentry.captureUserFeedback(userFeedback)
- Timber.i(
- "Submitted user feedback: name %s email %s comment %s",
- nameString[0],
- emailString[0],
- description.text.toString()
- )
- runOnUiThread {
- Toast.makeText(
- this, R.string.sentry_dialogue_success, Toast.LENGTH_LONG
- ).show()
- }
- // Close the activity
- finish()
- // start the main activity
- startActivity(packageManager.getLaunchIntentForPackage(packageName))
- } catch (e: Exception) {
- Timber.e(e, "Failed to submit user feedback")
- // Show a toast if the user feedback could not be submitted
- runOnUiThread {
- Toast.makeText(
- this, R.string.sentry_dialogue_failed_toast, Toast.LENGTH_LONG
- ).show()
- }
- }
- }.start()
- }
- // get restart button
- findViewById(R.id.restart).setOnClickListener { _: View? ->
- // Restart the app and submit sans feedback
- val sentryException = intent.getSerializableExtra("sentryException") as Throwable?
- if (crashReportingEnabled) Sentry.captureException(sentryException!!)
- finish()
- startActivity(packageManager.getLaunchIntentForPackage(packageName))
- }
- } else if (!crashReportingEnabled) {
- // set feedback_text to "Crash reporting is disabled"
- (findViewById(R.id.feedback_text) as MaterialTextView).setText(R.string.sentry_enable_nag)
- submit.setOnClickListener { _: View? ->
- Toast.makeText(
- this, R.string.sentry_dialogue_disabled, Toast.LENGTH_LONG
- ).show()
- }
- // handle restart button
- // we have to explicitly enable it because it's disabled by default
- findViewById(R.id.restart).isEnabled = true
- findViewById(R.id.restart).setOnClickListener { _: View? ->
- // Restart the app
- finish()
- startActivity(packageManager.getLaunchIntentForPackage(packageName))
- }
- }
// handle reset button
findViewById(R.id.reset).setOnClickListener { _: View? ->
// show a confirmation material dialog
diff --git a/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt
index 458d3c3..21b647e 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/MainActivity.kt
@@ -35,7 +35,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.room.Room
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
-import com.fox2code.foxcompat.view.FoxDisplay
import com.fox2code.mmm.AppUpdateManager.Companion.appUpdateManager
import com.fox2code.mmm.OverScrollManager.OverScrollHelper
import com.fox2code.mmm.androidacy.AndroidacyRepoData
@@ -65,7 +64,8 @@ import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.textfield.TextInputEditText
-import org.matomo.sdk.extra.TrackHelper
+import ly.count.android.sdk.Countly
+import ly.count.android.sdk.ModuleFeedback.FeedbackCallback
import timber.log.Timber
import java.sql.Timestamp
@@ -118,7 +118,7 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
onMainActivityCreate(this)
super.onCreate(savedInstanceState)
INSTANCE = this
- TrackHelper.track().screen(this).with(MainApplication.INSTANCE!!.tracker)
+
// 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) {
throw RuntimeException("This is not an official build of AMM")
@@ -143,8 +143,11 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
db.close()
if (enabledRepos.isNotEmpty()) {
enabledRepos.delete(enabledRepos.length - 2, enabledRepos.length)
- TrackHelper.track().event("Enabled Repos", enabledRepos.toString())
- .with(MainApplication.INSTANCE!!.tracker)
+ // use countly to track enabled repos
+ val repoMap = HashMap()
+ repoMap["repos"] = enabledRepos.toString()
+ Countly.sharedInstance().events().recordEvent("enabled_repos",
+ repoMap as Map?, 1)
}
}.start()
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000)
@@ -235,7 +238,9 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
// filter the appropriate list based on visibility
if (initMode) return
val query = s.toString()
- TrackHelper.track().search(query).with(MainApplication.INSTANCE!!.tracker)
+ Countly.sharedInstance().events().recordEvent("search", HashMap().apply {
+ put("query", query)
+ } as Map?, 1)
Thread {
if (moduleViewListBuilder.setQueryChange(query)) {
Timber.i("Query submit: %s on offline list", query)
@@ -261,7 +266,9 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// filter the appropriate list based on visibility
val query = textInputEditText.text.toString()
- TrackHelper.track().search(query).with(MainApplication.INSTANCE!!.tracker)
+ Countly.sharedInstance().events().recordEvent("search", HashMap().apply {
+ put("query", query)
+ } as Map?, 1)
Thread {
if (moduleViewListBuilder.setQueryChange(query)) {
Timber.i("Query submit: %s on offline list", query)
@@ -396,7 +403,6 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
}
}
})
- textInputEditText.minimumHeight = FoxDisplay.dpToPixel(16f)
textInputEditText.imeOptions =
EditorInfo.IME_ACTION_SEARCH or EditorInfo.IME_FLAG_NO_FULLSCREEN
@@ -405,8 +411,6 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
when (item.itemId) {
R.id.settings_menu_item -> {
- TrackHelper.track().event("view_list", "settings")
- .with(MainApplication.INSTANCE!!.tracker)
// start settings activity so that when user presses back, they go back to main activity and on api34 they see a preview of the main activity. tell settings activity current active tab so that it can be selected when user goes back to main activity
val intent = Intent(this@MainActivity, SettingsActivity::class.java)
when (bottomNavigationView.selectedItemId) {
@@ -417,8 +421,6 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
}
R.id.online_menu_item -> {
- TrackHelper.track().event("view_list", "online_modules")
- .with(MainApplication.INSTANCE!!.tracker)
searchTextInputEditText!!.clearFocus()
searchTextInputEditText!!.text?.clear()
// set module_list_online as visible and module_list as gone. fade in/out
@@ -439,8 +441,6 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
}
R.id.installed_menu_item -> {
- TrackHelper.track().event("view_list", "installed_modules")
- .with(MainApplication.INSTANCE!!.tracker)
searchTextInputEditText!!.clearFocus()
searchTextInputEditText!!.text?.clear()
// set module_list_online as gone and module_list as visible. fade in/out
@@ -747,6 +747,44 @@ class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!)
moduleViewListBuilderOnline.applyTo(moduleListOnline!!, moduleViewAdapterOnline!!)
}, "Repo update thread").start()
+ if (MainApplication.shouldShowFeedback()) {
+ Countly.sharedInstance().feedback()
+ .getAvailableFeedbackWidgets { retrievedWidgets, error ->
+ if (error == null) {
+ if (retrievedWidgets.size > 0) {
+ val feedbackWidget = retrievedWidgets[0]
+ Countly.sharedInstance().feedback().presentFeedbackWidget(
+ feedbackWidget,
+ this@MainActivity,
+ "Close",
+ object : FeedbackCallback {
+ override fun onClosed() {
+ }
+
+ // maybe show a toast when the widget is closed
+ override fun onFinished(error: String) {
+ // error handling here
+ if (error.isNotEmpty()) {
+ Toast.makeText(
+ this@MainActivity,
+ "Error: $error",
+ Toast.LENGTH_LONG
+ ).show()
+ } else {
+ Toast.makeText(
+ this@MainActivity,
+ "Feedback sent",
+ Toast.LENGTH_LONG
+ ).show()
+ }
+ }
+ })
+ }
+ } else {
+ Timber.e(error)
+ }
+ }
+ }
}
fun maybeShowUpgrade() {
diff --git a/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt b/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt
index 8ae4637..e629e69 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/MainApplication.kt
@@ -17,6 +17,7 @@ import android.content.pm.PackageManager
import android.content.res.Resources
import android.os.Build
import android.os.Bundle
+import android.os.Process
import android.os.SystemClock
import android.util.Log
import androidx.annotation.StyleRes
@@ -28,7 +29,6 @@ import androidx.emoji2.text.EmojiCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import androidx.work.Configuration
-import com.fox2code.foxcompat.app.internal.FoxProcessExt
import com.fox2code.mmm.installer.InstallerInitializer
import com.fox2code.mmm.installer.InstallerInitializer.Companion.peekMagiskVersion
import com.fox2code.mmm.manager.LocalModuleInfo
@@ -37,8 +37,6 @@ import com.fox2code.mmm.utils.TimberUtils.configTimber
import com.fox2code.mmm.utils.io.FileUtils
import com.fox2code.mmm.utils.io.GMSProviderInstaller.Companion.installIfNeeded
import com.fox2code.mmm.utils.io.net.Http.Companion.getHttpClientWithCache
-import com.fox2code.mmm.utils.sentry.SentryMain
-import com.fox2code.mmm.utils.sentry.SentryMain.initialize
import com.fox2code.rosettax.LanguageSwitcher
import com.google.common.hash.Hashing
import com.topjohnwu.superuser.Shell
@@ -46,12 +44,8 @@ import io.noties.markwon.Markwon
import io.noties.markwon.html.HtmlPlugin
import io.noties.markwon.image.ImagesPlugin
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler
-import io.sentry.Sentry
-import io.sentry.SentryLevel
-import org.matomo.sdk.Matomo
-import org.matomo.sdk.Tracker
-import org.matomo.sdk.TrackerBuilder
-import org.matomo.sdk.extra.TrackHelper
+import ly.count.android.sdk.Countly
+import ly.count.android.sdk.CountlyConfig
import timber.log.Timber
import java.io.File
import java.security.SecureRandom
@@ -60,6 +54,7 @@ import java.util.Date
import java.util.Random
import kotlin.math.abs
+
@Suppress("unused", "MemberVisibilityCanBePrivate")
class MainApplication : Application(), Configuration.Provider, ActivityLifecycleCallbacks {
@@ -97,18 +92,6 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
return field
}
private var existingKey: CharArray? = null
-
- var tracker: Tracker? = null
- get() {
- if (field == null) {
- field = TrackerBuilder.createDefault(BuildConfig.ANALYTICS_ENDPOINT, 1)
- .build(Matomo.getInstance(this))
- val tracker = field!!
- tracker.startNewSession()
- tracker.dispatchInterval = 1000
- }
- return field
- }
private var makingNewKey = false
private var isCrashHandler = false
@@ -216,6 +199,27 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
get() = !this.isLightTheme
override fun onCreate() {
+
+ Thread.setDefaultUncaughtExceptionHandler { _: Thread?, throwable: Throwable ->
+ clearCachedSharedPrefs()
+ // open crash handler and exit
+ val intent = Intent(this, CrashHandler::class.java)
+ // pass the entire exception to the crash handler
+ intent.putExtra("exception", throwable)
+ // add stacktrace as string
+ intent.putExtra("stacktrace", throwable.stackTrace)
+ // serialize Sentry.captureException and pass it to the crash handler
+ intent.putExtra("sentryException", throwable)
+ // pass crashReportingEnabled to crash handler
+ intent.putExtra("crashReportingEnabled", isCrashReportingEnabled)
+ // add isCrashing to intent
+ intent.putExtra("isCrashing", true)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ Timber.e("Starting crash handler")
+ startActivity(intent)
+ Timber.e("Exiting")
+ Process.killProcess(Process.myPid())
+ }
supportedLocales.addAll(
listOf(
"ar",
@@ -254,7 +258,6 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
Timber.e(e, "Failed to register activity lifecycle callbacks")
}
}
- initialize(this)
// Initialize Timber
configTimber()
Timber.i(
@@ -276,19 +279,25 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
if (BuildConfig.DEBUG) Timber.d("Started from background: %s", !isInForeground)
if (BuildConfig.DEBUG) Timber.d("AMM is running in debug mode")
// analytics
- if (BuildConfig.DEBUG) Timber.d("Initializing matomo")
- if (!isMatomoAllowed()) {
- if (BuildConfig.DEBUG) Timber.d("Matomo is not allowed")
- tracker!!.isOptOut = true
- } else {
- tracker!!.isOptOut = false
- }
- if (getSharedPreferences("matomo")!!.getBoolean("install_tracked", false)) {
- TrackHelper.track().download().with(INSTANCE!!.tracker)
- if (BuildConfig.DEBUG) Timber.d("Sent install event to matomo")
- getSharedPreferences("matomo")!!.edit().putBoolean("install_tracked", true).apply()
+ if (BuildConfig.DEBUG) Timber.d("Initializing countly")
+ if (!analyticsAllowed()) {
+ if (BuildConfig.DEBUG) Timber.d("countly is not allowed")
} else {
- if (BuildConfig.DEBUG) Timber.d("Matomo already has install")
+ val config = CountlyConfig(
+ this,
+ "ff1dc022295f64a7a5f6a5ca07c0294400c71b60",
+ "https://ctly.androidacy.com"
+ )
+ if (isCrashReportingEnabled) {
+ config.enableCrashReporting()
+ }
+ config.enableAutomaticViewTracking()
+ config.setPushIntentAddMetadata(true)
+ config.setLoggingEnabled(BuildConfig.DEBUG)
+ config.setRequiresConsent(false)
+ config.setRecordAppStartTime(true)
+ Countly.sharedInstance().init(config)
+ Countly.applicationOnCreate()
}
try {
@Suppress("DEPRECATION") @SuppressLint("PackageManagerGetSignatures") val s =
@@ -454,17 +463,12 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
// prepend [TAINTED] to the message
message = "[TAINTED] $message"
}
- if (priority >= Log.WARN) {
+ if (priority >= Log.ERROR) {
if (t != null) {
Log.println(priority, tag, message)
t.printStackTrace()
- Sentry.captureException(t)
} else {
Log.println(priority, tag, message)
- when (priority) {
- Log.ERROR -> Sentry.captureMessage(message, SentryLevel.ERROR)
- Log.WARN -> Sentry.captureMessage(message, SentryLevel.WARNING)
- }
}
}
}
@@ -481,8 +485,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
// Is application wrapped, and therefore must reduce it's feature set.
@SuppressLint("RestrictedApi") // Use FoxProcess wrapper helper.
- @JvmField
- val isWrapped = !FoxProcessExt.isRootLoader()
+ const val isWrapped = false
private val callers = ArrayList()
@JvmField
@@ -656,7 +659,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
}
val isCrashReportingEnabled: Boolean
- get() = SentryMain.IS_SENTRY_INSTALLED && getSharedPreferences("mmm")!!.getBoolean(
+ get() = getSharedPreferences("mmm")!!.getBoolean(
"pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING
)
val bootSharedPreferences: SharedPreferences?
@@ -670,11 +673,37 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
val isNotificationPermissionGranted: Boolean
get() = NotificationManagerCompat.from((INSTANCE)!!).areNotificationsEnabled()
- fun isMatomoAllowed(): Boolean {
+ fun analyticsAllowed(): Boolean {
return getSharedPreferences("mmm")!!.getBoolean(
"pref_analytics_enabled", BuildConfig.DEFAULT_ENABLE_ANALYTICS
)
}
+
+ fun shouldShowFeedback(): Boolean {
+ // should not have been shown in 30 days and only 1 in 5 chance
+ return if (getSharedPreferences("mmm")!!.getBoolean("pref_feedback_shown", false)) {
+ false
+ } else {
+ val random = Random()
+ val chance = random.nextInt(5)
+ if (chance == 0) {
+ val lastFeedback = getSharedPreferences("mmm")!!.getLong(
+ "pref_last_feedback", 0
+ )
+ val now = System.currentTimeMillis()
+ if (now - lastFeedback > 2592000000L) {
+ val editor = getSharedPreferences("mmm")!!.edit()
+ editor.putLong("pref_last_feedback", now)
+ editor.apply()
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
+ }
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
@@ -683,6 +712,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
}
override fun onActivityStarted(activity: Activity) {
+ Countly.sharedInstance().onStart(activity)
}
override fun onActivityResumed(activity: Activity) {
@@ -694,6 +724,7 @@ class MainApplication : Application(), Configuration.Provider, ActivityLifecycle
}
override fun onActivityStopped(activity: Activity) {
+ Countly.sharedInstance().onStop()
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
diff --git a/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt b/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt
index e892d49..8c293ce 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/NotificationType.kt
@@ -4,7 +4,6 @@
@file:Suppress(
"KotlinConstantConditions",
- "UNINITIALIZED_ENUM_COMPANION_WARNING",
"ktConcatNullable"
)
@@ -207,14 +206,15 @@ enum class NotificationType(
compatActivity.cacheDir, "installer" + File.separator + "module.zip"
)
IntentHelper.openFileTo(compatActivity, module) { d: File, u: Uri, s: Int ->
+ val companion = NotificationType.Companion
if (s == IntentHelper.RESPONSE_FILE) {
try {
- if (needPatch(d)) {
+ if (companion.needPatch(d)) {
patchModuleSimple(
read(d), FileOutputStream(d)
)
}
- if (needPatch(d)) {
+ if (companion.needPatch(d)) {
if (d.exists() && !d.delete()) Timber.w("Failed to delete non module zip")
Toast.makeText(
compatActivity, R.string.invalid_format, Toast.LENGTH_SHORT
diff --git a/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt
index c884cf8..5c1c7a0 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/SetupActivity.kt
@@ -150,18 +150,41 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
}
(Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check)) as MaterialSwitch).isChecked =
BuildConfig.ENABLE_AUTO_UPDATER
- (Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting)) as MaterialSwitch).isChecked =
+ 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
// pref_crash_reporting_pii
- (Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting_pii)) as MaterialSwitch).isChecked =
+ crashReportingPii.isChecked =
BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING_PII
// pref_analytics_enabled
- (Objects.requireNonNull(view.findViewById(R.id.setup_app_analytics)) as MaterialSwitch).isChecked =
+ analyticsEnabled.isChecked =
BuildConfig.DEFAULT_ENABLE_ANALYTICS
+ // if analytics is disabled, force disable crash reporting
+ if (!view.findViewById(R.id.setup_app_analytics).isChecked) {
+ setupCrashReporting.isEnabled = false
+ crashReportingPii.isEnabled = false
+ setupCrashReporting.isChecked = false
+ crashReportingPii.isChecked = false
+ }
+ // listen for changes to the analytics switch
+ analyticsEnabled.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
+ // if analytics is disabled, force disable crash reporting
+ if (!isChecked) {
+ setupCrashReporting.isChecked = false
+ crashReportingPii.isChecked = false
+ setupCrashReporting.isEnabled = false
+ crashReportingPii.isEnabled = false
+ } else {
+ setupCrashReporting.isEnabled = true
+ crashReportingPii.isEnabled = true
+ }
+ }
// assert that both switches match the build config on debug builds
if (BuildConfig.DEBUG) {
assert((Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check)) as MaterialSwitch).isChecked == BuildConfig.ENABLE_AUTO_UPDATER)
- assert((Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting)) as MaterialSwitch).isChecked == BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING)
+ assert(setupCrashReporting.isChecked == BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING)
}
// Repos are a little harder, as the enabled_repos build config is an arraylist
val andRepoView =
@@ -178,7 +201,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
isChecked
)
}
- (Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting)) as MaterialSwitch).setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
+ setupCrashReporting.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
Timber.i(
"Crash Reporting: %s",
isChecked
@@ -302,7 +325,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
// Set the crash reporting pref
editor.putBoolean(
"pref_crash_reporting",
- (Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting)) as MaterialSwitch).isChecked
+ setupCrashReporting.isChecked
)
// Set the crash reporting PII pref
editor.putBoolean(
@@ -339,7 +362,7 @@ class SetupActivity : AppCompatActivity(), LanguageActivity {
reposListDao.setEnabled(androidacyRepoRoomObj.id, androidacyRepoRoom)
reposListDao.setEnabled(magiskAltRepoRoomObj.id, magiskAltRepoRoom)
db.close()
- editor.putString("last_shown_setup", "v4")
+ editor.putString("last_shown_setup", "v5")
// Commit the changes
editor.commit()
// Log the changes
diff --git a/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt b/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt
index a7666d2..9c43f57 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/UpdateActivity.kt
@@ -26,7 +26,6 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.textview.MaterialTextView
import org.json.JSONException
-import org.matomo.sdk.extra.TrackHelper
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
@@ -64,9 +63,6 @@ class UpdateActivity : AppCompatActivity() {
}
}
chgWv = findViewById(R.id.changelog_webview)
- if (MainApplication.isMatomoAllowed()) {
- TrackHelper.track().screen(this).with(MainApplication.INSTANCE!!.tracker)
- }
val changelogWebView = chgWv!!
val webSettings = changelogWebView.settings
webSettings.userAgentString = Http.androidacyUA
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 638379c..11b296b 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyActivity.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyActivity.kt
@@ -46,7 +46,6 @@ import com.fox2code.mmm.utils.io.net.Http.Companion.doHttpGet
import com.fox2code.mmm.utils.io.net.Http.Companion.hasWebView
import com.fox2code.mmm.utils.io.net.Http.Companion.markCaptchaAndroidacySolved
import com.google.android.material.progressindicator.LinearProgressIndicator
-import org.matomo.sdk.extra.TrackHelper
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.File
@@ -76,7 +75,7 @@ class AndroidacyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
moduleFile = File(this.cacheDir, "module.zip")
super.onCreate(savedInstanceState)
- TrackHelper.track().screen(this).with(MainApplication.INSTANCE!!.tracker)
+
val intent = this.intent
var uri: Uri? = intent.data
@Suppress("KotlinConstantConditions")
diff --git a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyUtil.kt b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyUtil.kt
index 0330566..5554b0e 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyUtil.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyUtil.kt
@@ -8,15 +8,13 @@ package com.fox2code.mmm.androidacy
import android.net.Uri
import com.fox2code.mmm.BuildConfig
-import com.fox2code.mmm.utils.io.net.Http.Companion.doHttpGet
-import java.io.IOException
@Suppress("MemberVisibilityCanBePrivate", "MemberVisibilityCanBePrivate")
enum class AndroidacyUtil {
;
companion object {
- const val REFERRER = "utm_source=FoxMMM&utm_medium=app"
+ const val REFERRER = "utm_source=AMMM&utm_medium=app"
fun isAndroidacyLink(uri: Uri?): Boolean {
return uri != null && isAndroidacyLink(uri.toString(), uri)
}
@@ -109,39 +107,5 @@ enum class AndroidacyUtil {
}
return null
}
-
- /**
- * Check if the url is a premium direct download link
- * @param url url to check
- * @return true if it is a premium direct download link
- * @noinspection unused
- */
- fun isPremiumDirectDownloadLink(url: String): Boolean {
- return url.contains("/magisk/ddl/")
- }
-
- /**
- * Returns the markdown directly from the API for rendering. Premium only, and internal testing only currently.
- * /#blocked-by: A#F-0815
- * @param url URL to get markdown from
- * @return String of markdown
- * @noinspection unused
- */
- fun getMarkdownFromAPI(url: String?): String? {
- val md: ByteArray = try {
- doHttpGet(url!!, false)
- } catch (ignored: IOException) {
- return null
- }
- return String(md)
- }
-
- fun getMarkdownForModule(moduleId: String): String? {
- try {
- return getMarkdownFromAPI("https://production-api.androidacy.com/magisk/$moduleId/markdown")
- } catch (ignored: IOException) {
- }
- return null
- }
}
}
\ No newline at end of file
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 8c5ebb2..c213207 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyWebAPI.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/androidacy/AndroidacyWebAPI.kt
@@ -16,7 +16,6 @@ import android.webkit.JavascriptInterface
import android.widget.Toast
import androidx.annotation.Keep
import androidx.core.content.ContextCompat
-import com.fox2code.foxcompat.view.FoxDisplay
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
@@ -158,7 +157,7 @@ class AndroidacyWebAPI(
return@injectButton null
}
}, "androidacy_repo")
- val dim5dp = FoxDisplay.dpToPixel(5f)
+ val dim5dp = activity.resources.getDimensionPixelSize(R.dimen.dim5dp)
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp)
activity.runOnUiThread {
val alertDialog = builder.show()
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 d0c3dfc..92fd25e 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/background/BackgroundUpdateChecker.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/background/BackgroundUpdateChecker.kt
@@ -378,7 +378,7 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
fun onMainActivityCreate(context: Context) {
// Refuse to run if first_launch pref is not false
if (MainApplication.getSharedPreferences("mmm")!!
- .getString("last_shown_setup", null) != "v4"
+ .getString("last_shown_setup", null) != "v5"
) return
// create notification channel group
val groupName: CharSequence = context.getString(R.string.notification_group_updates)
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 dedcfca..ab8ed0e 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerActivity.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerActivity.kt
@@ -46,8 +46,6 @@ import com.fox2code.mmm.utils.io.Files.Companion.write
import com.fox2code.mmm.utils.io.Hashes.Companion.checkSumMatch
import com.fox2code.mmm.utils.io.PropUtils
import com.fox2code.mmm.utils.io.net.Http
-import com.fox2code.mmm.utils.sentry.SentryBreadcrumb
-import com.fox2code.mmm.utils.sentry.SentryMain
import com.google.android.material.bottomnavigation.BottomNavigationItemView
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -56,8 +54,8 @@ import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.superuser.io.SuFile
+import ly.count.android.sdk.Countly
import org.apache.commons.compress.archivers.zip.ZipFile
-import org.matomo.sdk.extra.TrackHelper
import timber.log.Timber
import java.io.BufferedReader
import java.io.File
@@ -89,7 +87,7 @@ class InstallerActivity : AppCompatActivity() {
moduleCache = File(this.cacheDir, "installer")
if (!moduleCache!!.exists() && !moduleCache!!.mkdirs()) Timber.e("Failed to mkdir module cache dir!")
super.onCreate(savedInstanceState)
- TrackHelper.track().screen(this).with(MainApplication.INSTANCE!!.tracker)
+
val intent = this.intent
val target: String
val name: String?
@@ -131,15 +129,9 @@ class InstallerActivity : AppCompatActivity() {
finish()
return
}
- // Note: Sentry only send this info on crash.
if (MainApplication.isCrashReportingEnabled) {
- val breadcrumb = SentryBreadcrumb()
- breadcrumb.setType("install")
- breadcrumb.setData("target", target)
- breadcrumb.setData("name", name)
- breadcrumb.setData("checksum", checksum)
- breadcrumb.setCategory("app.action.preinstall")
- SentryMain.addSentryBreadcrumb(breadcrumb)
+ // set countly breadcrumb
+ Countly.sharedInstance().crashes().addCrashBreadcrumb("InstallerActivity.onCreate")
}
val urlMode = target.startsWith("http://") || target.startsWith("https://")
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
@@ -183,9 +175,18 @@ class InstallerActivity : AppCompatActivity() {
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Fox:Installer")
prgInd?.visibility = View.VISIBLE
if (urlMode) installerTerminal!!.addLine("- Downloading $name")
- TrackHelper.track().event("installer_start", name).with(MainApplication.INSTANCE!!.tracker)
+ // track install
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("install", mapOf(
+ "name" to name,
+ "url" to target,
+ "checksum" to checksum,
+ "noExtensions" to noExtensions,
+ "rootless" to rootless,
+ "mmtReborn" to mmtReborn
+ ))
+ }
Thread(Runnable {
-
// ensure module cache is is in our cache dir
if (urlMode && !moduleCache!!.absolutePath.startsWith(MainApplication.INSTANCE!!.cacheDir.absolutePath)) throw SecurityException(
"Module cache is not in cache dir!"
@@ -533,18 +534,7 @@ class InstallerActivity : AppCompatActivity() {
}
// Note: Sentry only send this info on crash.
if (MainApplication.isCrashReportingEnabled) {
- val breadcrumb = SentryBreadcrumb()
- breadcrumb.setType("install")
- breadcrumb.setData("moduleId", if (moduleId == null) "" else moduleId)
- breadcrumb.setData("mmtReborn", if (mmtReborn) "true" else "false")
- breadcrumb.setData("isAnyKernel3", if (anyKernel3) "true" else "false")
- breadcrumb.setData("noExtensions", if (noExtensions) "true" else "false")
- breadcrumb.setData("magiskCmdLine", if (magiskCmdLine) "true" else "false")
- breadcrumb.setData(
- "ansi", if (installerTerminal!!.isAnsiEnabled) "enabled" else "disabled"
- )
- breadcrumb.setCategory("app.action.install")
- SentryMain.addSentryBreadcrumb(breadcrumb)
+ Countly.sharedInstance().crashes().addCrashBreadcrumb("InstallerActivity.doInstall")
}
if (mmtReborn && magiskCmdLine) {
Timber.w("mmtReborn and magiskCmdLine may not work well together")
diff --git a/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt b/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt
index ee0f6a1..574f795 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/installer/InstallerInitializer.kt
@@ -11,6 +11,7 @@ import com.fox2code.mmm.NotificationType
import com.fox2code.mmm.utils.io.Files.Companion.existsSU
import com.topjohnwu.superuser.NoShellException
import com.topjohnwu.superuser.Shell
+import ly.count.android.sdk.Countly
import timber.log.Timber
import java.io.File
@@ -125,7 +126,7 @@ class InstallerInitializer {
if (Shell.isAppGrantedRoot() == null || !Shell.isAppGrantedRoot()!!) {
// if Shell.isAppGrantedRoot() == null loop until it's not null
return if (Shell.isAppGrantedRoot() == null) {
- Thread.sleep(100)
+ Thread.sleep(150)
tryGetMagiskPath(forceCheck)
} else {
null
@@ -162,33 +163,53 @@ class InstallerInitializer {
if (BuildConfig.DEBUG) {
Timber.i("Magisk path 1: %s", mgskPth)
}
- } else if (Shell.cmd("if [ -d /data/adb/ksu ] && [ -f /data/adb/ksud ]; then echo true; else echo false; fi", "su -V").to(
+ } else if (Shell.cmd("if [ -d /data/adb/ksu ]; then echo true; else echo false; fi", "su -V").to(
output
).exec().isSuccess && "true" == output[0]
) {
- mgskPth = "/data/adb"
- isKsu = true
+ // check su -v for kernelsu
+ val suVer: ArrayList = ArrayList()
+ Shell.cmd("su -v").to(suVer).exec()
+ if (suVer.size > 0 && suVer[0].contains("ksu") || suVer[0].contains("Kernelsu", true)) {
+ if (BuildConfig.DEBUG) {
+ Timber.i("Kernelsu detected")
+ }
+ mgskPth = "/data/adb"
+ isKsu = true
+ // if analytics enabled, set breadcrumb for countly
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().crashes().addCrashBreadcrumb("ksu detected")
+ }
+ } else {
+ if (BuildConfig.DEBUG) {
+ Timber.e("[ANOMALY] Kernelsu not detected but /data/adb/ksu exists")
+ }
+ return null
+ }
+ } else {
+ if (BuildConfig.DEBUG) {
+ Timber.e("Failed to get Magisk path")
+ }
+ return null
}
Timber.i("Magisk runtime path: %s", mgskPth)
mgskVerCode = output[1].toInt()
Timber.i("Magisk version code: %s", mgskVerCode)
- if (mgskPth != null) {
- if (mgskVerCode >= Constants.MAGISK_VER_CODE_FLAT_MODULES && mgskVerCode < Constants.MAGISK_VER_CODE_PATH_SUPPORT && (mgskPth.isEmpty() || !File(
- mgskPth
- ).exists())
- ) {
- mgskPth = "/sbin"
- }
+ if (mgskVerCode >= Constants.MAGISK_VER_CODE_FLAT_MODULES && mgskVerCode < Constants.MAGISK_VER_CODE_PATH_SUPPORT && (mgskPth.isEmpty() || !File(
+ mgskPth
+ ).exists())
+ ) {
+ mgskPth = "/sbin"
}
- if (mgskPth != null) {
- if (mgskPth.isNotEmpty() && existsSU(File(mgskPth))) {
- Companion.mgskPth = mgskPth
- } else {
- Timber.e("Failed to get Magisk path (Got $mgskPth)")
- mgskPth = null
- }
+ if (mgskPth.isNotEmpty() && existsSU(File(mgskPth))) {
+ Companion.mgskPth = mgskPth
} else {
- Timber.e("Failed to get Magisk path (Got null or other)")
+ Timber.e("Failed to get Magisk path (Got $mgskPth)")
+ mgskPth = null
+ }
+ // if mgskPth is null, but we're granted root, log an error
+ if (mgskPth == null && Shell.isAppGrantedRoot() == true) {
+ Timber.e("[ANOMALY] Failed to get Magisk path but granted root")
}
Companion.mgskVerCode = mgskVerCode
return mgskPth
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 cc827d3..f0bff13 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/manager/ModuleManager.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/manager/ModuleManager.kt
@@ -17,7 +17,7 @@ import com.fox2code.mmm.utils.room.ModuleListCacheDatabase
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileInputStream
-import org.matomo.sdk.extra.TrackHelper
+import ly.count.android.sdk.Countly
import timber.log.Timber
import java.io.BufferedReader
import java.io.IOException
@@ -30,9 +30,9 @@ class ModuleManager private constructor() : SyncManager() {
private var updatableModuleCount = 0
override fun scanInternal(updateListener: UpdateListener) {
- // if last_shown_setup is not "v4", then refuse to continue
+ // if last_shown_setup is not "v5", then refuse to continue
if (MainApplication.getSharedPreferences("mmm")!!
- .getString("last_shown_setup", "") != "v4"
+ .getString("last_shown_setup", "") != "v5"
) {
return
}
@@ -145,9 +145,12 @@ class ModuleManager private constructor() : SyncManager() {
if (modulesList.isNotEmpty()) {
modulesList.deleteCharAt(modulesList.length - 1)
}
- // send list to matomo
- TrackHelper.track().event("installed_modules", modulesList.toString())
- .with(MainApplication.INSTANCE!!.tracker)
+ // send list to countly if analytics is enabled
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("modules", HashMap().apply {
+ put("modules", modulesList.toString())
+ })
+ }
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG) Timber.d("Scan update")
val modulesUpdate = SuFile("/data/adb/modules_update").list()
if (modulesUpdate != null) {
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 5e98e57..78325dd 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/markdown/MarkdownActivity.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/markdown/MarkdownActivity.kt
@@ -25,7 +25,6 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import org.matomo.sdk.extra.TrackHelper
import timber.log.Timber
import java.io.IOException
import java.nio.charset.StandardCharsets
@@ -35,7 +34,7 @@ class MarkdownActivity : AppCompatActivity() {
private var footer: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- TrackHelper.track().screen(this).with(MainApplication.INSTANCE!!.tracker)
+
val intent = this.intent
if (!MainApplication.checkSecret(intent)) {
Timber.e("Impersonation detected!")
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 a4ff066..0f5cd73 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/module/ActionButtonType.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/module/ActionButtonType.kt
@@ -11,7 +11,6 @@ import android.text.Spanned
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.DrawableRes
-import com.fox2code.foxcompat.view.FoxDisplay
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.MainApplication.Companion.INSTANCE
@@ -31,7 +30,7 @@ import com.fox2code.mmm.utils.IntentHelper.Companion.openUrlAndroidacy
import com.google.android.material.chip.Chip
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.noties.markwon.Markwon
-import org.matomo.sdk.extra.TrackHelper
+import ly.count.android.sdk.Countly
import timber.log.Timber
import java.util.Objects
@@ -50,7 +49,12 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("view_notes", name).with(INSTANCE!!.tracker)
+ // if analytics is enabled, track the event
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_description", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
val notesUrl = moduleHolder.repoModule?.notesUrl
if (isAndroidacyLink(notesUrl)) {
try {
@@ -145,7 +149,10 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("view_update_install", name).with(INSTANCE!!.tracker)
+ // send event to countly
+ Countly.sharedInstance().events().recordEvent("view_update_install", HashMap().apply {
+ put("module", name ?: "null")
+ })
// if text is reinstall, we need to uninstall first - warn the user but don't proceed
if (moduleHolder.moduleInfo != null) {
// get the text
@@ -216,7 +223,9 @@ enum class ActionButtonType {
{ Uri.parse(updateZipUrl) },
moduleHolder.updateZipRepo
)
- val dim5dp = FoxDisplay.dpToPixel(5f)
+ val dim5dp = INSTANCE!!.lastActivity?.resources!!.getDimensionPixelSize(
+ R.dimen.dim5dp
+ )
builder.setBackgroundInsetStart(dim5dp).setBackgroundInsetEnd(dim5dp)
val alertDialog = builder.show()
for (i in -3..-1) {
@@ -255,7 +264,12 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("uninstall_module", name).with(INSTANCE!!.tracker)
+ // if analytics is enabled, track the event
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_uninstall", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
Timber.i(Integer.toHexString(moduleHolder.moduleInfo?.flags ?: 0))
if (!instance!!.setUninstallState(
moduleHolder.moduleInfo!!, !moduleHolder.hasFlag(
@@ -310,7 +324,11 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("config_module", name).with(INSTANCE!!.tracker)
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_config", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
if (isAndroidacyLink(config)) {
openUrlAndroidacy(button.context, config, true)
} else {
@@ -333,7 +351,11 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("support_module", name).with(INSTANCE!!.tracker)
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_support", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
openUrl(button.context, Objects.requireNonNull(moduleHolder.mainModuleInfo.support))
}
},
@@ -352,7 +374,11 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("donate_module", name).with(INSTANCE!!.tracker)
+if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_donate", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
openUrl(button.context, moduleHolder.mainModuleInfo.donate)
}
},
@@ -368,7 +394,11 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("warning_module", name).with(INSTANCE!!.tracker)
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_warning", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
MaterialAlertDialogBuilder(button.context).setTitle(R.string.warning)
.setMessage(R.string.warning_message).setPositiveButton(
R.string.understand
@@ -390,7 +420,11 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
- TrackHelper.track().event("safe_module", name).with(INSTANCE!!.tracker)
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_safe", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
MaterialAlertDialogBuilder(button.context).setTitle(R.string.safe_module)
.setMessage(R.string.safe_message).setPositiveButton(
R.string.understand
@@ -409,7 +443,11 @@ enum class ActionButtonType {
moduleHolder.repoModule?.moduleInfo?.name
}
// positive button executes install logic and says reinstall. negative button does nothing
- TrackHelper.track().event("remote_module", name).with(INSTANCE!!.tracker)
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_update_install", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
val madb = MaterialAlertDialogBuilder(button.context)
madb.setTitle(R.string.remote_module)
val moduleInfo: ModuleInfo = if (moduleHolder.mainModuleInfo != null) {
@@ -462,8 +500,11 @@ enum class ActionButtonType {
moduleHolder.repoModule?.moduleInfo?.name
}
if (BuildConfig.DEBUG) Timber.d("doAction: remote module for %s", name)
- TrackHelper.track().event("view_update_install", name)
- .with(INSTANCE!!.tracker)
+ if (MainApplication.analyticsAllowed()) {
+ Countly.sharedInstance().events().recordEvent("view_update_install", HashMap().apply {
+ put("module", name ?: "null")
+ })
+ }
// Androidacy manage the selection between download and install
if (isAndroidacyLink(updateZipUrl)) {
if (BuildConfig.DEBUG) Timber.d("Androidacy link detected")
@@ -518,7 +559,9 @@ enum class ActionButtonType {
{ Uri.parse(updateZipUrl) },
moduleHolder.updateZipRepo
)
- val dim5dp = FoxDisplay.dpToPixel(5f)
+ val dim5dp = INSTANCE!!.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/ModuleViewAdapter.kt b/app/src/main/kotlin/com/fox2code/mmm/module/ModuleViewAdapter.kt
index fc14398..7a3ae44 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/module/ModuleViewAdapter.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/module/ModuleViewAdapter.kt
@@ -26,7 +26,6 @@ import androidx.annotation.StringRes
import androidx.cardview.widget.CardView
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.RecyclerView
-import com.fox2code.foxcompat.view.FoxDisplay
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
@@ -74,7 +73,6 @@ class ModuleViewAdapter : RecyclerView.Adapter() {
private val creditText: TextView
private val descriptionText: TextView
private val moduleOptionsHolder: HorizontalScrollView
- private val moduleLayoutHelper: TextView
private val updateText: TextView
private val actionsButtons: Array
private val actionButtonsTypes: ArrayList
@@ -93,7 +91,6 @@ class ModuleViewAdapter : RecyclerView.Adapter() {
creditText = itemView.findViewById(R.id.credit_text)
descriptionText = itemView.findViewById(R.id.description_text)
moduleOptionsHolder = itemView.findViewById(R.id.module_options_holder)
- moduleLayoutHelper = itemView.findViewById(R.id.module_layout_helper)
updateText = itemView.findViewById(R.id.updated_text)
actionsButtons = arrayOfNulls(6)
actionsButtons[0] = itemView.findViewById(R.id.button_action1)
@@ -194,7 +191,6 @@ class ModuleViewAdapter : RecyclerView.Adapter() {
}
creditText.visibility = View.VISIBLE
moduleOptionsHolder.visibility = View.VISIBLE
- moduleLayoutHelper.visibility = View.VISIBLE
descriptionText.visibility = View.VISIBLE
val moduleInfo = moduleHolder.mainModuleInfo
moduleInfo.verify()
@@ -249,7 +245,6 @@ class ModuleViewAdapter : RecyclerView.Adapter() {
descriptionText.text = moduleInfo.description
}
val updateText = moduleHolder.updateTimeText
- var hasUpdateText = true
if (updateText.isNotEmpty()) {
val repoModule = moduleHolder.repoModule
this.updateText.visibility = View.VISIBLE
@@ -267,7 +262,6 @@ ${getString(R.string.module_repo)} ${moduleHolder.repoName}""" + if ((repoModule
this.updateText.setText(R.string.substratum_builtin_module)
} else {
this.updateText.visibility = View.GONE
- hasUpdateText = false
}
actionButtonsTypes.clear()
moduleHolder.getButtons(itemView.context, actionButtonsTypes, showCaseMode)
@@ -290,12 +284,6 @@ ${getString(R.string.module_repo)} ${moduleHolder.repoName}""" + if ((repoModule
}
if (actionButtonsTypes.isEmpty()) {
moduleOptionsHolder.visibility = View.GONE
- moduleLayoutHelper.visibility = View.GONE
- } else if (actionButtonsTypes.size > 2 || !hasUpdateText) {
- moduleLayoutHelper.minHeight = FoxDisplay.dpToPixel(36f)
- .coerceAtLeast(moduleOptionsHolder.height - FoxDisplay.dpToPixel(14f))
- } else {
- moduleLayoutHelper.minHeight = FoxDisplay.dpToPixel(4f)
}
cardView.isClickable = false
if (moduleHolder.isModuleHolder && moduleHolder.hasFlag(ModuleInfo.FLAG_MODULE_ACTIVE)) {
@@ -316,7 +304,6 @@ ${getString(R.string.module_repo)} ${moduleHolder.repoName}""" + if ((repoModule
switchMaterial.visibility = View.GONE
creditText.visibility = View.GONE
moduleOptionsHolder.visibility = View.GONE
- moduleLayoutHelper.visibility = View.GONE
descriptionText.visibility = View.GONE
updateText.visibility = View.GONE
titleText.text = " "
@@ -331,7 +318,9 @@ ${getString(R.string.module_repo)} ${moduleHolder.repoName}""" + if ((repoModule
}
if (type === ModuleHolder.Type.NOTIFICATION) {
val notificationType = moduleHolder.notificationType
- titleText.setText(notificationType?.textId ?: 0)
+ if (notificationType?.textId != null) {
+ titleText.setText(notificationType.textId)
+ }
// set title text appearance
titleText.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyLarge)
if (notificationType != null) {
@@ -353,7 +342,9 @@ ${getString(R.string.module_repo)} ${moduleHolder.repoName}""" + if ((repoModule
}
}
if (type === ModuleHolder.Type.SEPARATOR) {
- titleText.setText(if (moduleHolder.separator != null) moduleHolder.separator.title else 0)
+ if (moduleHolder.separator != null) {
+ titleText.text = getString(moduleHolder.separator.title)
+ }
}
if (DEBUG) {
if (vType != 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 fe69014..d35964a 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/repo/CustomRepoManager.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/repo/CustomRepoManager.kt
@@ -29,7 +29,7 @@ class CustomRepoManager internal constructor(
init {
repoCount = 0
// refuse to load if setup is not complete
- if (getSharedPreferences("mmm")!!.getString("last_shown_setup", "") == "v4") {
+ if (getSharedPreferences("mmm")!!.getString("last_shown_setup", "") == "v5") {
val i = 0
val lastFilled = intArrayOf(0)
// now the same as above but for room database
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 0382a0c..d3fe7e4 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/repo/RepoManager.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/repo/RepoManager.kt
@@ -56,7 +56,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
repoData = LinkedHashMap()
modules = HashMap()
// refuse to load if setup is not complete
- if (getSharedPreferences("mmm")!!.getString("last_shown_setup", "") == "v4") {
+ if (getSharedPreferences("mmm")!!.getString("last_shown_setup", "") == "v5") {
// We do not have repo list config yet.
androidacyRepoData = addAndroidacyRepoData()
val altRepo = addRepoData(MAGISK_ALT_REPO, "Magisk Modules Alt Repo")
@@ -82,8 +82,8 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
}
private fun populateDefaultCache(repoData: RepoData?) {
- // if last_shown_setup is not "v4", them=n refuse to continue
- if (getSharedPreferences("mmm")!!.getString("last_shown_setup", "") != "v4") {
+ // if last_shown_setup is not "v5", them=n refuse to continue
+ if (getSharedPreferences("mmm")!!.getString("last_shown_setup", "") != "v5") {
return
}
// make sure repodata is not null
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 bf2d597..65c88ac 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/settings/DebugFragment.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/settings/DebugFragment.kt
@@ -16,7 +16,6 @@ import com.fox2code.mmm.Constants
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
import com.fox2code.mmm.installer.InstallerInitializer
-import com.fox2code.mmm.utils.sentry.SentryMain
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.apache.commons.io.FileUtils
import timber.log.Timber
@@ -90,7 +89,7 @@ class DebugFragment : PreferenceFragmentCompat() {
}.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> }.show()
true
}
- if (!SentryMain.IS_SENTRY_INSTALLED || !BuildConfig.DEBUG || InstallerInitializer.peekMagiskPath() == null) {
+ if (!BuildConfig.DEBUG || InstallerInitializer.peekMagiskPath() == null) {
// Hide the pref_crash option if not in debug mode - stop users from purposely crashing the app
Timber.i(InstallerInitializer.peekMagiskPath())
findPreference("pref_test_crash")!!.isVisible = false
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 156da00..c5b81a1 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/settings/InfoFragment.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/settings/InfoFragment.kt
@@ -152,7 +152,7 @@ class InfoFragment : PreferenceFragmentCompat() {
// open androidacy
IntentHelper.openUrl(
MainApplication.INSTANCE!!.lastActivity!!,
- "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm_medium=app&utm_campaign=donate"
+ "https://www.androidacy.com/membership-join/?utm_source=AMMM&utm_medium=app&utm_campaign=donate"
)
true
}
@@ -164,7 +164,7 @@ class InfoFragment : PreferenceFragmentCompat() {
clipboard.setPrimaryClip(
ClipData.newPlainText(
toastText,
- "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm_medium=app&utm_campaign=donate"
+ "https://www.androidacy.com/membership-join/?utm_source=AMMM&utm_medium=app&utm_campaign=donate"
)
)
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show()
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 7b1e20e..7777d83 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/settings/PrivacyFragment.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/settings/PrivacyFragment.kt
@@ -17,7 +17,6 @@ import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
-import com.fox2code.mmm.utils.sentry.SentryMain
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import timber.log.Timber
import kotlin.system.exitProcess
@@ -53,7 +52,6 @@ class PrivacyFragment : PreferenceFragmentCompat() {
// Crash reporting
val crashReportingPreference =
findPreference("pref_crash_reporting")
- if (!SentryMain.IS_SENTRY_INSTALLED) crashReportingPreference!!.isVisible = false
crashReportingPreference!!.isChecked = MainApplication.isCrashReportingEnabled
val initialValue: Any = MainApplication.isCrashReportingEnabled
crashReportingPreference.onPreferenceChangeListener =
@@ -86,5 +84,16 @@ class PrivacyFragment : PreferenceFragmentCompat() {
materialAlertDialogBuilder.show()
true
}
+ // on pref_analytics_enabled change, update pref_crash_reporting (switch must be off and disabled if analytics is off)
+ val analyticsPreference = findPreference("pref_analytics_enabled")
+ analyticsPreference!!.onPreferenceChangeListener =
+ Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
+ if (initialValue === newValue) return@OnPreferenceChangeListener true
+ crashReportingPreference.isEnabled = newValue as Boolean
+ if (!newValue) crashReportingPreference.isChecked = false
+ true
+ }
+ // now, disable pref_crash_reporting if analytics is off
+ crashReportingPreference.isEnabled = analyticsPreference.isChecked
}
}
\ No newline at end of file
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 81d6a01..c8edea2 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/settings/RepoFragment.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/settings/RepoFragment.kt
@@ -24,8 +24,6 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import androidx.preference.TwoStatePreference
import androidx.room.Room
-import com.fox2code.foxcompat.view.FoxDisplay
-import com.fox2code.foxcompat.view.FoxViewCompat
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication
@@ -177,7 +175,7 @@ class RepoFragment : PreferenceFragmentCompat() {
// User clicked OK button. Open GitHub releases page
val browserIntent = Intent(
Intent.ACTION_VIEW,
- Uri.parse("https://www.androidacy.com/downloads/?view=FoxMMM&utm_source=FoxMMM&utm_medium=app&utm_campaign=FoxMMM")
+ Uri.parse("https://www.androidacy.com/downloads/?view=AMMM&utm_source=AMMM&utm_medium=app&utm_campaign=AMMM")
)
startActivity(browserIntent)
}.show()
@@ -623,9 +621,17 @@ class RepoFragment : PreferenceFragmentCompat() {
override fun afterTextChanged(s: Editable) {}
})
positiveButton.isEnabled = false
- val dp10 = FoxDisplay.dpToPixel(10f)
- val dp20 = FoxDisplay.dpToPixel(20f)
- FoxViewCompat.setMargin(input, dp20, dp10, dp20, dp10)
+ val dp10 = MainApplication.INSTANCE!!.lastActivity?.resources?.getDimensionPixelSize(
+ R.dimen.dp10
+ ) ?: 0
+ val dp20 = MainApplication.INSTANCE!!.lastActivity?.resources?.getDimensionPixelSize(
+ R.dimen.dp20
+ ) ?: 0
+ alertDialog.window!!.setSoftInputMode(20)
+ alertDialog.window!!.setLayout(
+ dp20,
+ dp10
+ )
true
}
}
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 f34129f..ecde152 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/settings/SettingsActivity.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/settings/SettingsActivity.kt
@@ -33,7 +33,6 @@ import com.fox2code.rosettax.LanguageActivity
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationBarView
import com.mikepenz.aboutlibraries.LibsBuilder
-import org.matomo.sdk.extra.TrackHelper
import timber.log.Timber
import java.sql.Timestamp
@@ -89,7 +88,7 @@ class SettingsActivity : AppCompatActivity(), LanguageActivity,
.commit()
true
}
- TrackHelper.track().screen(this).with(INSTANCE!!.tracker)
+
setContentView(R.layout.settings_activity)
setTitle(R.string.app_name_v2)
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000)
diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/ExternalHelper.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/ExternalHelper.kt
index 18a7828..35a133f 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/utils/ExternalHelper.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/utils/ExternalHelper.kt
@@ -15,7 +15,6 @@ import android.net.Uri
import android.os.Build
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
-import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Supplier
import com.fox2code.mmm.Constants
import com.topjohnwu.superuser.internal.UiThreadHandler
@@ -57,13 +56,6 @@ class ExternalHelper private constructor() {
fun openExternal(context: Context, uri: Uri?, repoId: String?): Boolean {
if (label == null) return false
- val param =
- ActivityOptionsCompat.makeCustomAnimation(
- context,
- rikka.core.R.anim.fade_in,
- rikka.core.R.anim.fade_out
- )
- .toBundle()
var intent = Intent(FOX_MMM_OPEN_EXTERNAL, uri)
intent.flags = IntentHelper.FLAG_GRANT_URI_PERMISSION
intent.putExtra(FOX_MMM_EXTRA_REPO_ID, repoId)
@@ -76,7 +68,7 @@ class ExternalHelper private constructor() {
if (multi) {
context.startActivity(intent)
} else {
- context.startActivity(intent, param)
+ context.startActivity(intent, null)
}
return true
} catch (e: ActivityNotFoundException) {
@@ -90,7 +82,7 @@ class ExternalHelper private constructor() {
}
intent.component = fallback
try {
- context.startActivity(intent, param)
+ context.startActivity(intent, null)
return true
} catch (e: ActivityNotFoundException) {
Timber.e(e)
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 b60ba72..13b9196 100644
--- a/app/src/main/kotlin/com/fox2code/mmm/utils/RuntimeUtils.kt
+++ b/app/src/main/kotlin/com/fox2code/mmm/utils/RuntimeUtils.kt
@@ -159,7 +159,7 @@ class RuntimeUtils {
if (BuildConfig.DEBUG) Timber.i("Checking if we need to run setup")
// Check if context is the first launch using prefs and if doSetupRestarting was passed in the intent
val prefs = MainApplication.getSharedPreferences("mmm")!!
- var firstLaunch = prefs.getString("last_shown_setup", null) != "v4"
+ var firstLaunch = prefs.getString("last_shown_setup", null) != "v5"
// First launch
// context is intentionally separate from the above if statement, because it needs to be checked even if the first launch check is true due to some weird edge cases
if (activity.intent.getBooleanExtra("doSetupRestarting", false)) {
@@ -264,7 +264,7 @@ class RuntimeUtils {
snackbar.setAction(R.string.upgrade_now) {
val intent = Intent(Intent.ACTION_VIEW)
intent.data =
- Uri.parse("https://androidacy.com/membership-join/#utm_source=foxmmm&utm_medium=app&utm_campaign=upgrade_snackbar")
+ Uri.parse("https://androidacy.com/membership-join/#utm_source=AMMM&utm_medium=app&utm_campaign=upgrade_snackbar")
activity.startActivity(intent)
}
snackbar.setAnchorView(R.id.bottom_navigation)
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 3d6b08b..f176e51 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
@@ -29,7 +29,7 @@ import com.fox2code.mmm.installer.InstallerInitializer.Companion.peekMagiskPath
import com.fox2code.mmm.installer.InstallerInitializer.Companion.peekMagiskVersion
import com.fox2code.mmm.utils.io.Files.Companion.makeBuffer
import com.google.net.cronet.okhttptransport.CronetInterceptor
-import io.sentry.android.okhttp.SentryOkHttpInterceptor
+import ly.count.android.sdk.Countly
import okhttp3.Cache
import okhttp3.Dns
import okhttp3.HttpUrl.*
@@ -53,11 +53,13 @@ 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
import java.nio.charset.StandardCharsets
import java.util.Objects
+import java.util.UUID.randomUUID
import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess
@@ -290,9 +292,9 @@ enum class Http {;
// User-Agent format was agreed on telegram
androidacyUA = if (hasWebView) {
WebSettings.getDefaultUserAgent(mainApplication)
- .replace("wv", "") + " FoxMMM/" + BuildConfig.VERSION_CODE
+ .replace("wv", "") + " AMMM/" + 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" + " FoxMmm/" + BuildConfig.VERSION_CODE
+ "Mozilla/5.0 (Linux; Android " + Build.VERSION.RELEASE + "; " + Build.DEVICE + ")" + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Mobile Safari/537.36" + " AMMM/" + BuildConfig.VERSION_CODE
}
httpclientBuilder.addInterceptor(Interceptor { chain: Interceptor.Chain? ->
val request: Request.Builder = chain!!.request().newBuilder()
@@ -340,9 +342,6 @@ enum class Http {;
chain.proceed(request.build())
})
- // add sentry interceptor
- httpclientBuilder.addInterceptor(SentryOkHttpInterceptor())
-
// Add cronet interceptor
// init cronet
try {
@@ -457,6 +456,10 @@ enum class Http {;
if (url.isEmpty()) {
throw IOException("Empty URL")
}
+ val uniqid = randomUUID().toString()
+ if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
+ Countly.sharedInstance().apm().startNetworkRequest(uniqid, url)
+ }
var response: Response?
response = try {
(if (allowCache) getHttpClientWithCache() else getHttpClient())!!.newCall(
@@ -465,7 +468,7 @@ enum class Http {;
).get().build()
).execute()
} catch (e: IOException) {
- Timber.e(e, "Failed to post %s", url)
+ 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 {
@@ -540,6 +543,11 @@ enum class Http {;
if (BuildConfig.DEBUG) Timber.d("doHttpGet: returning " + responseBody.contentLength() + " bytes")
}
}
+ if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
+ Countly.sharedInstance().apm().endNetworkRequest(uniqid, url, response!!.code,
+ 0, responseBody!!.contentLength().toInt()
+ )
+ }
return responseBody?.bytes() ?: ByteArray(0)
}
@@ -552,6 +560,11 @@ enum class Http {;
@Throws(IOException::class)
private fun doHttpPostRaw(url: String, data: String, allowCache: Boolean): Any {
if (BuildConfig.DEBUG) Timber.d("POST %s", url)
+
+ val uniqid = randomUUID().toString()
+ if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
+ Countly.sharedInstance().apm().startNetworkRequest(uniqid, url)
+ }
var response: Response?
try {
response =
@@ -625,11 +638,20 @@ enum class Http {;
response = response.cacheResponse
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()
+ )
+ }
return responseBody.bytes()
}
@Throws(IOException::class)
fun doHttpGet(url: String, progressListener: ProgressListener): ByteArray {
+ val uniqid = randomUUID().toString()
+ if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
+ Countly.sharedInstance().apm().startNetworkRequest(uniqid, url)
+ }
val response: Response
try {
response =
@@ -713,6 +735,11 @@ enum class Http {;
progressListener.onUpdate(
(downloaded / divider).toInt(), (target / divider).toInt(), true
)
+ if (MainApplication.analyticsAllowed() && MainApplication.isCrashReportingEnabled) {
+ Countly.sharedInstance().apm().endNetworkRequest(uniqid, url, response.code,
+ 0, responseBody.contentLength().toInt()
+ )
+ }
return byteArrayOutputStream.toByteArray()
}
diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/sentry/SentryBreadcrumb.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/sentry/SentryBreadcrumb.kt
deleted file mode 100644
index a0482d5..0000000
--- a/app/src/main/kotlin/com/fox2code/mmm/utils/sentry/SentryBreadcrumb.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
- */
-
-package com.fox2code.mmm.utils.sentry
-
-import io.sentry.Breadcrumb
-import io.sentry.SentryLevel
-import java.util.Objects
-
-class SentryBreadcrumb {
- val breadcrumb: Breadcrumb = Breadcrumb()
-
- init {
- breadcrumb.level = SentryLevel.INFO
- }
-
- fun setType(type: String?) {
- breadcrumb.type = type
- }
-
- fun setData(key: String, value: Any?) {
- @Suppress("NAME_SHADOWING") var value = value
- if (value == null) value = "null"
- Objects.requireNonNull(key)
- breadcrumb.setData(key, value)
- }
-
- fun setCategory(category: String?) {
- breadcrumb.category = category
- }
-}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/fox2code/mmm/utils/sentry/SentryMain.kt b/app/src/main/kotlin/com/fox2code/mmm/utils/sentry/SentryMain.kt
deleted file mode 100644
index ebe18e2..0000000
--- a/app/src/main/kotlin/com/fox2code/mmm/utils/sentry/SentryMain.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
- */
-
-package com.fox2code.mmm.utils.sentry
-
-import android.annotation.SuppressLint
-import android.content.Intent
-import android.content.SharedPreferences
-import android.net.Uri
-import android.os.Process
-import com.fox2code.mmm.BuildConfig
-import com.fox2code.mmm.CrashHandler
-import com.fox2code.mmm.MainApplication
-import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.hideToken
-import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.isAndroidacyLink
-import com.fox2code.mmm.utils.io.net.HttpException
-import io.sentry.Breadcrumb
-import io.sentry.Hint
-import io.sentry.Sentry
-import io.sentry.SentryEvent
-import io.sentry.SentryLevel
-import io.sentry.SentryOptions.BeforeBreadcrumbCallback
-import io.sentry.SentryOptions.BeforeSendCallback
-import io.sentry.android.core.SentryAndroid
-import io.sentry.android.core.SentryAndroidOptions
-import io.sentry.android.fragment.FragmentLifecycleIntegration
-import io.sentry.android.timber.SentryTimberIntegration
-import io.sentry.protocol.SentryId
-import org.matomo.sdk.extra.TrackHelper
-import timber.log.Timber
-
-object SentryMain {
- const val IS_SENTRY_INSTALLED = true
- private var isCrashing = false
- private var isSentryEnabled = false
- private var crashExceptionId: SentryId? = null
-
- /**
- * Initialize Sentry
- * Sentry is used for crash reporting and performance monitoring.
- */
- @JvmStatic
- @SuppressLint("RestrictedApi", "UnspecifiedImmutableFlag", "ApplySharedPref")
- fun initialize(mainApplication: MainApplication) {
- Thread.setDefaultUncaughtExceptionHandler { _: Thread?, throwable: Throwable ->
- isCrashing = true
- MainApplication.clearCachedSharedPrefs()
- TrackHelper.track().exception(throwable).with(MainApplication.INSTANCE!!.tracker)
- // open crash handler and exit
- val intent = Intent(mainApplication, CrashHandler::class.java)
- // pass the entire exception to the crash handler
- intent.putExtra("exception", throwable)
- // add stacktrace as string
- intent.putExtra("stacktrace", throwable.stackTrace)
- // serialize Sentry.captureException and pass it to the crash handler
- intent.putExtra("sentryException", throwable)
- // pass crashReportingEnabled to crash handler
- intent.putExtra("crashReportingEnabled", isSentryEnabled)
- // add isCrashing to intent
- intent.putExtra("isCrashing", true)
- // add crashExceptionId to intent
- if (crashExceptionId != null) {
- intent.putExtra("lastEventId", crashExceptionId!!.toString())
- } else {
- intent.putExtra("lastEventId", "")
- }
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
- Timber.e("Starting crash handler")
- mainApplication.startActivity(intent)
- Timber.e("Exiting")
- Process.killProcess(Process.myPid())
- }
- // If first_launch pref is not false, refuse to initialize Sentry
- val sharedPreferences = MainApplication.getSharedPreferences("mmm")!!
- if (sharedPreferences.getString("last_shown_setup", null) != "v4") {
- return
- }
- isSentryEnabled = sharedPreferences.getBoolean("pref_crash_reporting_enabled", false)
- // set sentryEnabled on preference change of pref_crash_reporting_enabled
- sharedPreferences.registerOnSharedPreferenceChangeListener { sharedPreferences1: SharedPreferences, s: String? ->
- if (s !== null && s == "pref_crash_reporting_enabled") {
- isSentryEnabled = sharedPreferences1.getBoolean(s, false)
- }
- }
- SentryAndroid.init(mainApplication) { options: SentryAndroidOptions ->
- // If crash reporting is disabled, stop here.
- if (!MainApplication.isCrashReportingEnabled) {
- isSentryEnabled = false // Set sentry state to disabled
- options.dsn = ""
- } else {
- // get pref_crash_reporting_pii pref
- val crashReportingPii = sharedPreferences.getBoolean("crashReportingPii", false)
- isSentryEnabled = true // Set sentry state to enabled
- options.addIntegration(
- FragmentLifecycleIntegration(
- mainApplication,
- enableFragmentLifecycleBreadcrumbs = true,
- enableAutoFragmentLifecycleTracing = true
- )
- )
- // Enable automatic activity lifecycle breadcrumbs
- options.isEnableActivityLifecycleBreadcrumbs = true
- // Enable automatic fragment lifecycle breadcrumbs
- options.addIntegration(SentryTimberIntegration())
- options.isCollectAdditionalContext = true
- options.isAttachThreads = true
- options.isAttachStacktrace = true
- options.isEnableNdk = true
- options.addInAppInclude("com.fox2code.mmm")
- options.addInAppInclude("com.fox2code.mmm.debug")
- options.addInAppInclude("com.fox2code.mmm.fdroid")
- options.addInAppExclude("com.fox2code.mmm.utils.sentry.SentryMain")
- options.addInAppInclude("com.fox2code.mmm.utils")
- // Respect user preference for sending PII. default is true on non fdroid builds, false on fdroid builds
- options.isSendDefaultPii = crashReportingPii
- options.enableAllAutoBreadcrumbs(true)
- // in-app screenshots are only sent if the app crashes, and it only shows the last activity. so no, we won't see your, ahem, "private" stuff
- options.isAttachScreenshot = true
- // It just tell if sentry should ping the sentry dsn to tell the app is running. Useful for performance and profiling.
- options.isEnableAutoSessionTracking = true
- // disable crash tracking - we handle that ourselves
- options.isEnableUncaughtExceptionHandler = true
- // Add a callback that will be used before the event is sent to Sentry.
- // With this callback, you can modify the event or, when returning null, also discard the event.
- options.environment = BuildConfig.BUILD_TYPE
- options.beforeSend = BeforeSendCallback { event: SentryEvent?, _: Hint? ->
- // in the rare event that crash reporting has been disabled since we started the app, we don't want to send the crash report
- if (!isSentryEnabled) {
- return@BeforeSendCallback null
- }
- crashExceptionId = event?.eventId
- // if debug build, log everything, but for release only log errors
- if (!BuildConfig.DEBUG) {
- if (event?.level == SentryLevel.DEBUG || event?.level == SentryLevel.INFO || event?.level == SentryLevel.WARNING) {
- return@BeforeSendCallback null
- }
- }
- // remove all failed to fetch data messages
- if (event?.message?.message?.contains("Failed to fetch") == true || event?.message?.message?.contains(
- "Failed to load"
- ) == true
- ) {
- return@BeforeSendCallback null
- }
- // for httpexception, do not send if error is 401, 403, 404, 429
- // get exception from event
- val exception = event?.throwable ?: return@BeforeSendCallback event
- // check status code
- if (exception is HttpException) {
- if (exception.errorCode in intArrayOf(401, 403, 404, 429)) {
- return@BeforeSendCallback null
- }
- }
- // if exception is null, return event
- event
- }
- // Filter breadcrumb content from crash report.
- options.beforeBreadcrumb =
- BeforeBreadcrumbCallback { breadcrumb: Breadcrumb, _: Hint? ->
- if (!isSentryEnabled) {
- return@BeforeBreadcrumbCallback null
- }
- val url = breadcrumb.getData("url") as String?
- if ("cloudflare-dns.com" == Uri.parse(url).host) {
- return@BeforeBreadcrumbCallback null
- }
- if (isAndroidacyLink(url)) {
- url?.let { hideToken(it) }?.let { breadcrumb.setData("url", it) }
- }
- breadcrumb
- }
- }
- }
- }
-
- fun addSentryBreadcrumb(sentryBreadcrumb: SentryBreadcrumb) {
- if (MainApplication.isCrashReportingEnabled) {
- Sentry.addBreadcrumb(sentryBreadcrumb.breadcrumb)
- }
- }
-}
diff --git a/app/src/main/res/layout/activity_crash_handler.xml b/app/src/main/res/layout/activity_crash_handler.xml
index 9029d5c..dd8c48a 100644
--- a/app/src/main/res/layout/activity_crash_handler.xml
+++ b/app/src/main/res/layout/activity_crash_handler.xml
@@ -2,20 +2,29 @@
~ 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.
-->
-
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:padding="4dp">
-
+
+
-
-
+
@@ -79,110 +88,36 @@
android:padding="4dp" />
-
+
+
+
+
+ android:layout_gravity="center|bottom"
+ android:orientation="horizontal">
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+ android:text="@string/restart" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_margin="10dp"
+ android:text="@string/reset_app" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_setup.xml b/app/src/main/res/layout/activity_setup.xml
index 104da18..48057d9 100644
--- a/app/src/main/res/layout/activity_setup.xml
+++ b/app/src/main/res/layout/activity_setup.xml
@@ -11,7 +11,6 @@
android:layout_height="match_parent"
android:layout_margin="0dp"
android:padding="0dp"
- app:fitsSystemWindowsInsets="start|end|bottom|top"
tools:context=".SetupActivity">
-
-
-
+
+
-
-
+
+
+ android:padding="1dp"
+ app:layout_constraintTop_toTopOf="parent">
+
+ android:gravity="fill"
+ android:orientation="horizontal">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ android:textSize="19sp" />
-
+
-
+ android:layout_margin="2dp"
+ android:background="@null"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_baseline_delete_forever_24"
+ android:textSize="16sp"
+ tools:ignore="RtlHardcoded,TouchTargetSizeCheck" />
-
-
-
-
-
+
+
+ android:layout_marginVertical="2dp"
+ android:autoLink="all"
+ android:padding="4dp"
+ android:text="@string/loading"
+ android:textAppearance="?attr/textAppearanceBodyMedium"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="16sp" />
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:text="@string/loading"
+ android:visibility="gone"
+ app:chipIcon="@drawable/ic_baseline_error_24" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/webview.xml b/app/src/main/res/layout/webview.xml
index d031877..40efd99 100644
--- a/app/src/main/res/layout/webview.xml
+++ b/app/src/main/res/layout/webview.xml
@@ -6,8 +6,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- app:fitsSystemWindowsInsets="start|end|bottom|top">
+ android:orientation="vertical">
السماح بإرسال معلومات إضافية في تقارير الأعطال ، والتي قد يحتوي بعضها على معلومات تعريف شخصية مثل عنوان IP ومعرفات الجهاز.
قد يشمل ذلك معرفات الجهاز وعناوين IP. لن يتم استخدام أي بيانات لأي غرض آخر بخلاف تحليل الأعطال وتحسين الأداء.
خطأ في الوصول إلى WebView. قد تتأثر الوظيفة.
- اسمح لنا بتتبع استخدام التطبيق وعمليات التثبيت. متوافق تمامًا مع إجمالي الناتج المحلي ويستخدم Matomo ، الذي مخزن عن طريق Androidacy.
+ اسمح لنا بتتبع استخدام التطبيق وعمليات التثبيت. متوافق تمامًا مع إجمالي الناتج المحلي ويستخدم Countly ، الذي مخزن عن طريق Androidacy.
لم تتم ترجمة اللغة %s. أتود ترجمتها ؟
إنشاء تأثير تمويه (Blur) خلف بعض الحوارات والعناصر. لاحظ أن التمويه (Blur) قد لا يعمل بشكل جيد على بعض الأجهزة وقد لا يعمل مع الجميع.
حدث خطأ أثناء قراءة الخصائص المشتركة. الرجاء إعادة تعيين التطبيق.
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index abfcaac..7de68d7 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -304,7 +304,7 @@
Je vyžadováná URL
Repozitář už existuje.
K aktivaci režimu prezentace je vyžadován restart aplikace.
- Umožněte nám sledovat používání a instalace aplikací. Plně kompatibilní s GDPR a používá Matomo, hostované společností Androidacy.
+ Umožněte nám sledovat používání a instalace aplikací. Plně kompatibilní s GDPR a používá Countly, hostované společností Androidacy.
Novinky a aktualizace
Ladění
Zpátky
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 4ef7cbc..b0dee6f 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -319,7 +319,7 @@
Απαίτηση wi-fi ή δικτύου χωρίς μετρήσεις για έλεγχο ενημερώσεων. Προτείνεται να είναι σε λειτουργία αν έχετε περιορισμένα δεδομένα κινητής τηλεφωνίας.
Συνέχισε να πατάς, ώστε να γίνεις δεκτός στο Hogwarts!
Η γλώσσα %s δεν έχει μεταφραστεί. Μπορείτε να μας βοηθήσετε στην μετάφρασή της;
- Επιτρέψτε μας να παρακολουθούμε τη χρήση και τις εγκαταστάσεις εφαρμογών. Πλήρως συμβατό με το GDPR και χρησιμοποιεί το Matomo, που φιλοξενείται από το Androidacy.
+ Επιτρέψτε μας να παρακολουθούμε τη χρήση και τις εγκαταστάσεις εφαρμογών. Πλήρως συμβατό με το GDPR και χρησιμοποιεί το Countly, που φιλοξενείται από το Androidacy.
Προσδιορίστε μια έκδοση από ενημερώσεις modules που θα θέλατε να αγνοήσετε. Παρακαλείσθε να χρησιμοποιήσετε μόνο τον κωδικό έκδοσης. Μπορείτε να βρείτε αυτόν τον κωδικό κρατώντας πατημένη την έκδοση στην λίστα με τα modules. Χρησιμοποιήστε ^ στην αρχή για να αναφερθείτε στην καθορισμένη έκδοση και σε όλες τις νεότερες. Χρησιμοποιήστε $ στο τέλος για να αναφερθείτε σε όλες τις εκδόσεις πριν την καθορισμένη.
Το stacktrace μπορεί να βρεθεί παρακάτω. Ωστόσο, ανεπιφύλακτα σας συνιστούμε να χρησιμοποιήσετε την παρακάτω φόρμα σχολίων για να υποβάλετε σχόλια. Με αυτόν τον τρόπο, αντί να αντιγράψετε χειροκίνητα το stacktrace, θα μας αποσταλεί αυτόματα. Επίσης, συμβάλλετε στην αποσυμφόρηση με αυτόν τον τρόπο και επιπλέον λεπτομέρειες αναφέρονται αυτόματα.
Επανεγκατάσταση
diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml
index b8b503c..46cd969 100644
--- a/app/src/main/res/values-es-rMX/strings.xml
+++ b/app/src/main/res/values-es-rMX/strings.xml
@@ -313,7 +313,7 @@
Permite enviar información adicional en reportes de fallos, algunos de los cuales pueden contener información de identificación personal, como la dirección IP y los identificadores de dispositivo.
Esto puede incluir identificadores de dispositivo y direcciones IP. No se utilizarán datos para ningún otro propósito que no sea analizar fallas y mejorar el rendimiento.
Se requiere reiniciar la aplicación para activar el modo de exhibición.
- Permítanos rastrear el uso y las instalaciones de la aplicación. Totalmente compatible con GDPR y utiliza Matomo, alojado por Androidacy.
+ Permítanos rastrear el uso y las instalaciones de la aplicación. Totalmente compatible con GDPR y utiliza Countly, alojado por Androidacy.
Reinstalar
Tenga en cuenta que es posible que algunas configuraciones no surtan efecto hasta que reinicie la aplicación.
¡Use el código copiado para obtener la mitad de descuento en su primer mes!
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 1221239..5811a14 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -292,7 +292,7 @@
Se requiere una URL
Este repositorio ya existe.
Se requiere reiniciar la aplicación para habilitar el modo de presentación.
- Permítenos usar el seguimiento de uso e instalaciones. Totalmente compatible con el RGPD y utiliza Matomo, alojado por Androidacy.
+ Permítenos usar el seguimiento de uso e instalaciones. Totalmente compatible con el RGPD y utiliza Countly, alojado por Androidacy.
Novedades y actualizaciones
Depuración
Atrás
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index ef8bc6a..73c14df 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -309,7 +309,7 @@
URL dibutuhkan
Repo sudah ada.
Mulai ulang aplikasi dibutuhkan untuk mengaktifkan mode showcase.
- Mengizinkan kami untuk mencatat penggunaan aplikasi dan pemasangan. Sepenuhnya mengikuti GDPR dan menggunakan Matomo, di hosting oleh Androidacy.
+ Mengizinkan kami untuk mencatat penggunaan aplikasi dan pemasangan. Sepenuhnya mengikuti GDPR dan menggunakan Countly, di hosting oleh Androidacy.
Men-debug
Berita dan pembaruan
Kembali
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 21137bc..103fa83 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -310,7 +310,7 @@
Errore nell\'accesso a WebView. La funzionalità potrebbe non essere disponibile.
L\'URL è richiesto
La repository esiste già.
- Ci consente di tracciare l\'utilizzo della app e la quantità di installazioni. Conforme al GDPR, usa Matomo, un servizio offerto da Androidacy.
+ Ci consente di tracciare l\'utilizzo della app e la quantità di installazioni. Conforme al GDPR, usa Countly, un servizio offerto da Androidacy.
Debug
Notizie e aggiornamenti
Indietro
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 37835aa..ac1a19a 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -299,7 +299,7 @@
URL が必要です
リポジトリがすでに存在します。
ショーケースモードを有効化するにはアプリを再起動してください。
- アプリの使用状況とインストール済みモジュールの追跡を許可します。GDPR に完全に準拠し、Androidacy によって運営されている Matomo を使用します。
+ アプリの使用状況とインストール済みモジュールの追跡を許可します。GDPR に完全に準拠し、Androidacy によって運営されている Countly を使用します。
デバッグ
ニュースとアップデート
戻る
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 4b5195a..baf400b 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -295,7 +295,7 @@
URL is vereist
Repo bestaat al.
Een herstart van de app is vereist om de showcase-modus in te schakelen.
- Sta ons toe app-gebruik en installaties bij te houden. Volledig GDPR-compatibel en maakt gebruik van Matomo, gehost door Androidacy.
+ Sta ons toe app-gebruik en installaties bij te houden. Volledig GDPR-compatibel en maakt gebruik van Countly, gehost door Androidacy.
Nieuws en updates
Debugging
Ga terug
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 8b32a9f..b1e7298 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -307,7 +307,7 @@
Tworzy efekt rozmycia za niektórymi oknami dialogowymi i elementami. Może nie działać dobrze na niektórych urządzeniach.
Wystąpił błąd podczas odczytywania preferencji współdzielonych. Proszę zresetować aplikację.
Aby włączyć tryb blokady, wymagany jest restart aplikacji.
- Pozwól nam śledzić wykorzystanie aplikacji i instalacje. W pełni zgodne z GDPR i używa Matomo, hostowanego przez Androidacy.
+ Pozwól nam śledzić wykorzystanie aplikacji i instalacje. W pełni zgodne z GDPR i używa Countly, hostowanego przez Androidacy.
Wiadomości i aktualizacje
Wróć
Debugowanie
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index b5c53d6..26190f3 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -311,7 +311,7 @@
Exigir wifi ou uma conexão ilimitada para buscar por atualizações
Isso irá limpar o cache do aplicativo. Suas preferências serão salvas, mas o aplicativo poderá demorar para realizar algumas operações temporariamente.
Uma reinicialização é necessária para ativar o modo showcase.
- Nos permite monitorar o uso e instalação do app. Totalmente conforme o GDPR, utilizando Matomo, hospedado pelo Androidacy.
+ Nos permite monitorar o uso e instalação do app. Totalmente conforme o GDPR, utilizando Countly, hospedado pelo Androidacy.
Depuração
Novidades e atualizações
Voltar
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index bc8ca2e..f8207f7 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -300,7 +300,7 @@
Você está configurando o aplicativo para usar um servidor de testes do Androidacy. Isso pode resultar em instabilidade no aplicativo e falha ao carregar o repositório online. NÃO reporte falhas se você tiver esta opção habilitada. O aplicativo será reinicializado para recarregar os repositórios.
Fox2Code é o desenvolvedor original do aplicativo. Sem ele, isso nunca seria possível.
Uma reinicialização é necessária para ativar o modo showcase.
- Nos permite monitorar o uso e instalação do app. Totalmente conforme o GDPR, utilizando Matomo, hospedado pelo Androidacy.
+ Nos permite monitorar o uso e instalação do app. Totalmente conforme o GDPR, utilizando Countly, hospedado pelo Androidacy.
Depuração
Novidades e atualizações
Voltar
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 100de41..8d30fe8 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -302,7 +302,7 @@
Создаёт эффект размытия позади некоторых диалоговых окон и элементов. Обратите внимание, что размытие может на некоторых устройствах работать плохо и не во всех местах.
Произошла ошибка при чтении общих настроек. Пожалуйста, перезагрузите приложение.
Для включения режима демонстрации требуется перезапуск приложения.
- Разрешите нам отслеживать использование и установки приложения. Полностью соответсвует GDPR и использует Matomo, размещенный на Androidacy.
+ Разрешите нам отслеживать использование и установки приложения. Полностью соответсвует GDPR и использует Countly, размещенный на Androidacy.
Отладка
Новости и обновления
Назад
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 9fc9dd4..7af387e 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -302,7 +302,7 @@
Відладка
Новини та оновлення
Назад
- Дозвольте нам відстежувати використання та встановлення додатку. Повністю відповідає GDPR і використовує Matomo, розміщений на Androidacy.
+ Дозвольте нам відстежувати використання та встановлення додатку. Повністю відповідає GDPR і використовує Countly, розміщений на Androidacy.
Донат на Fox2Code
Дотан на Androidacy
Придбайте преміальну підписку на Androidacy, щоб підтримувати додаток і репозиторій.
diff --git a/app/src/main/res/values-v31/themes.xml b/app/src/main/res/values-v31/themes.xml
index bcf58b6..a18e803 100644
--- a/app/src/main/res/values-v31/themes.xml
+++ b/app/src/main/res/values-v31/themes.xml
@@ -22,7 +22,7 @@