diff --git a/app/build.gradle b/app/build.gradle index 719ef57..642bf14 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ import io.sentry.android.gradle.extensions.InstrumentationFeature +import com.android.build.OutputFile plugins { // Gradle doesn't allow conditionally enabling/disabling plugins @@ -249,6 +250,41 @@ android { } } +ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3, 'arm64-v8a':4] + +// For per-density APKs, create a similar map: +// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3] + +// For each APK output variant, override versionCode with a combination of +// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode +// is equal to defaultConfig.versionCode. If you configure product flavors that +// define their own versionCode, variant.versionCode uses that value instead. +android.applicationVariants.all { variant -> + + // Assigns a different version code for each output APK + // other than the universal APK. + variant.outputs.each { output -> + + // Stores the value of ext.abiCodes that is associated with the ABI for this variant. + def baseAbiVersionCode = + // Determines the ABI for this variant and returns the mapped value. + project.ext.abiCodes.get(output.getFilter(OutputFile.ABI)) + + // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes, + // the following code doesn't override the version code for universal APKs. + // However, because you want universal APKs to have the lowest version code, + // this outcome is desirable. + if (baseAbiVersionCode != null) { + + // Assigns the new version code to versionCodeOverride, which changes the + // version code for only the output APK, not for the variant itself. Skipping + // this step causes Gradle to use the value of variant.versionCode for the APK. + output.versionCodeOverride = + baseAbiVersionCode * 1000 + variant.versionCode + } + } +} + aboutLibraries { additionalLicenses = ["LGPL_3_0_only", "Apache_2_0"] } @@ -261,9 +297,10 @@ configurations { // "true" is not allowed inside this block, use "hasSentryConfig" instead. // This is because gradle doesn't allow to enable/disable plugins conditionally sentry { - // Disable sentry on F-Droid flavor ignoredFlavors = [] + // All the symbol upload logic has to be disabled for f-droid flavor, as we don't have a way to provide an auth token to f-droid + // Disables or enables the handling of Proguard mapping for Sentry. // If enabled the plugin will generate a UUID and will take care of // uploading the mapping to Sentry. If disabled, all the logic @@ -298,7 +335,7 @@ sentry { // Does auto instrumentation for specified features through bytecode manipulation. // Default is enabled. tracingInstrumentation { - enabled = hasSentryConfig + enabled = true features = EnumSet.allOf(InstrumentationFeature) } @@ -320,7 +357,6 @@ configurations { dependencies { // UI implementation 'androidx.appcompat:appcompat:1.6.1' - //noinspection GradleDependency implementation 'androidx.activity:activity-ktx:1.7.0' implementation 'androidx.emoji2:emoji2:1.3.0' implementation 'androidx.emoji2:emoji2-views-helper:1.3.0' @@ -334,7 +370,6 @@ dependencies { implementation "dev.rikka.rikkax.insets:insets:1.3.0" implementation 'com.github.KieronQuinn:MonetCompat:0.4.1' implementation 'com.github.Fox2Code:FoxCompat:0.2.0' - // Update the version code in the root build.gradle implementation 'com.mikepenz:aboutlibraries:10.6.2' // Utils @@ -350,7 +385,7 @@ dependencies { // protobuf - fixes a crash on some devices implementation 'com.google.protobuf:protobuf-javalite:3.22.2' - implementation 'com.github.topjohnwu.libsu:io:5.0.1' + implementation 'com.github.topjohnwu.libsu:io:5.0.5' implementation 'com.github.Fox2Code:RosettaX:1.0.9' implementation 'com.github.Fox2Code:AndroidANSI:1.0.1' diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index 871e255..7b56ff9 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -607,11 +607,9 @@ public class MainApplication extends FoxApplication implements androidx.work.Con // Create a key to encrypt a realm and save it securely in the keystore public byte[] getNewKey() { - Timber.d("Creating a new key."); // check if we have a key already SharedPreferences sharedPreferences = MainApplication.getPreferences("realm_key"); if (sharedPreferences.contains("iv_and_encrypted_key")) { - Timber.v("Found a key in the keystore."); return getExistingKey(); } // open a connection to the android keystore @@ -685,10 +683,8 @@ public class MainApplication extends FoxApplication implements androidx.work.Con // Access the encrypted key in the keystore, decrypt it with the secret, // and use it to open and read from the realm again public byte[] getExistingKey() { - Timber.d("Accessing the existing key."); // attempt to read the existingKey property if (existingKey != null) { - Timber.v("Found an existing key in memory."); return existingKey; } // open a connection to the android keystore diff --git a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java index 2f75693..ae97471 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -14,6 +14,8 @@ import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.io.SuFile; import com.topjohnwu.superuser.io.SuFileInputStream; +import org.matomo.sdk.extra.TrackHelper; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -79,6 +81,7 @@ public final class ModuleManager extends SyncManager { Timber.e("using fallback instead."); } if (BuildConfig.DEBUG) Timber.d("Scan"); + StringBuilder modulesList = new StringBuilder(); if (modules != null) { for (String module : modules) { if (!new SuFile("/data/adb/modules/" + module).isDirectory()) @@ -151,8 +154,15 @@ public final class ModuleManager extends SyncManager { if (BuildConfig.DEBUG) Timber.d(e); moduleInfo.flags |= FLAG_MM_INVALID; } + // append moduleID:moduleName to the list + modulesList.append(moduleInfo.id).append(":").append(moduleInfo.versionCode).append(","); } } + if (modulesList.length() > 0) { + modulesList.deleteCharAt(modulesList.length() - 1); + } + // send list to matomo + TrackHelper.track().event("installed_modules", String.valueOf(modulesList)).with(MainApplication.getINSTANCE().getTracker()); if (BuildConfig.DEBUG) Timber.d("Scan update"); String[] modules_update = new SuFile("/data/adb/modules_update").list(); if (modules_update != null) { diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java index e1e56eb..fe6f3bd 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; import io.realm.Realm; import io.realm.RealmConfiguration; @@ -282,7 +283,24 @@ public class RepoData extends XRepo { @Override public boolean isEnabled() { - return this.enabled; + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + Realm realm2 = Realm.getInstance(realmConfiguration2); + AtomicBoolean dbEnabled = new AtomicBoolean(false); + realm2.executeTransaction(realm -> { + ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); + if (reposList != null) { + dbEnabled.set(reposList.isEnabled()); + } else { + // should never happen but for safety + dbEnabled.set(false); + } + }); + realm2.close(); + if (dbEnabled.get()) { + return !this.forceHide; + } else { + return false; + } } @Override diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index 4f7ffd4..7fc4e4b 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -215,6 +215,30 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { assert preferenceManager != null; setPreferencesFromResource(R.xml.root_preferences, rootKey); applyMaterial3(getPreferenceScreen()); + + // track enabled prefs + dataStore.getSharedPreferences().registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> { + // use trackHelper to log key and value + // get key and value first + String value = sharedPreferences.getString(key, null); + // then log + if (value != null) { + TrackHelper.track().event("pref_changed", key + "=" + value).with(MainApplication.getINSTANCE().getTracker()); + } + }); + // track all non empty values + SharedPreferences sharedPreferences = dataStore.getSharedPreferences(); + // disabled until EncryptedSharedPreferences fixes getAll() + /* StringBuilder keys = new StringBuilder(); + for (String key : sharedPreferences.getAll().keySet()) { + String value = sharedPreferences.getString(key, null); + if (value != null) { + keys.append(key).append(","); + } + } + if (keys.length() > 0) { + TrackHelper.track().event("prefs_all", keys.toString()).with(MainApplication.getINSTANCE().getTracker()); + }*/ // add bottom navigation bar to the settings BottomNavigationView bottomNavigationView = requireActivity().findViewById(R.id.bottom_navigation); if (bottomNavigationView != null) { @@ -566,7 +590,6 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { int i = 0; for (LocalModuleInfo localModuleInfo : localModuleInfos) { moduleNames[i] = localModuleInfo.name; - SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm"); // get the stringset pref_background_update_check_excludes Set stringSet = sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>()); // Stringset uses id, we show name @@ -576,7 +599,6 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { } new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.background_update_check_excludes).setMultiChoiceItems(moduleNames, checkedItems, (dialog, which, isChecked) -> { // get the stringset pref_background_update_check_excludes - SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm"); Set stringSet = new HashSet<>(sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>())); // get id from name String id; @@ -790,6 +812,19 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show(); return true; }); + // pref_announcements to https://t.me/androidacy + linkClickable = findPreference("pref_announcements"); + linkClickable.setOnPreferenceClickListener(p -> { + devModeStep = 0; + IntentHelper.openUrl(p.getContext(), "https://t.me/androidacy"); + return true; + }); + linkClickable.setOnPreferenceLongClickListener(p -> { + String toastText = requireContext().getString(R.string.link_copied); + clipboard.setPrimaryClip(ClipData.newPlainText(toastText, "https://t.me/androidacy")); + Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show(); + return true; + }); findPreference("pref_show_licenses").setOnPreferenceClickListener(p -> { devModeStep = devModeStep == 1 ? 2 : 0; BackgroundUpdateChecker.onMainActivityResume(this.requireContext()); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bbe0e6a..341373c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -404,4 +404,6 @@ Other By clicking "finish", you are agreeing to be bound by the LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.en.html) license and the EULA (https://www.androidacy.com/foxmmm-eula/) Allow us to track app usage and installs. Fully GDPR compliant and uses Matomo, hosted by Androidacy. + Debugging + News and updates diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index d1859bc..7bb9a48 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -187,6 +187,7 @@ + + + - - - + + + +