only support versionCode

would love to support version name but it's not going to work out very well

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/27/head
androidacy-user 2 years ago
parent e57829f21d
commit 159454b90e

@ -98,7 +98,11 @@ android {
// current timestamp of build
buildConfigField("long", "BUILD_TIME", "$timestamp")
// debug http requests. do not set this to true if you care about performance!!!!!
buildConfigField("boolean", "DEBUG_HTTP", "false")
if (System.getenv("CI") != null) {
buildConfigField("boolean", "DEBUG_HTTP", "true")
} else {
buildConfigField("boolean", "DEBUG_HTTP", "false")
}
// Latest commit hash as BuildConfig.COMMIT_HASH
buildConfigField("String", "COMMIT_HASH", "\"$gitCommitHash\"")
// Get the current branch name as BuildConfig.BRANCH_NAME
@ -161,7 +165,12 @@ android {
// current timestamp of build
buildConfigField("long", "BUILD_TIME", "$timestamp")
// debug http requests. do not set this to true if you care about performance!!!!!
buildConfigField("boolean", "DEBUG_HTTP", "false")
// if env var CI is set to true, this will be true
if (System.getenv("CI") != null) {
buildConfigField("boolean", "DEBUG_HTTP", "true")
} else {
buildConfigField("boolean", "DEBUG_HTTP", "false")
}
// Latest commit hash as BuildConfig.COMMIT_HASH
buildConfigField("String", "COMMIT_HASH", "\"$gitCommitHash\"")
// Get the current branch name as BuildConfig.BRANCH_NAME
@ -223,8 +232,11 @@ android {
// current timestamp of build
buildConfigField("long", "BUILD_TIME", "$timestamp")
// debug http requests. do not set this to true if you care about performance!!!!!
buildConfigField("boolean", "DEBUG_HTTP", "false")
if (System.getenv("CI") != null) {
buildConfigField("boolean", "DEBUG_HTTP", "true")
} else {
buildConfigField("boolean", "DEBUG_HTTP", "false")
}
// Latest commit hash as BuildConfig.COMMIT_HASH
buildConfigField("String", "COMMIT_HASH", "\"$gitCommitHash\"")
// Get the current branch name as BuildConfig.BRANCH_NAME
@ -469,6 +481,9 @@ dependencies {
// desugaring
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
// yes
implementation("com.github.fingerprintjs:fingerprint-android:2.0.0")
}
android {

@ -34,6 +34,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.fox2code.foxcompat.app.FoxActivity;
import com.fox2code.foxcompat.view.FoxDisplay;
import com.fox2code.mmm.androidacy.AndroidacyRepoData;
import com.fox2code.mmm.background.BackgroundUpdateChecker;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.manager.LocalModuleInfo;
@ -55,6 +56,7 @@ import org.matomo.sdk.extra.TrackHelper;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import io.realm.Realm;
import io.realm.RealmConfiguration;
@ -62,6 +64,7 @@ import timber.log.Timber;
public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRefreshListener, SearchView.OnQueryTextListener, SearchView.OnCloseListener, OverScrollManager.OverScrollHelper {
private static final int PRECISION = 10000;
private static MainActivity INSTANCE;
public static boolean doSetupNowRunning = true;
public static boolean doSetupRestarting = false;
public static List<LocalModuleInfo> localModuleInfoList = new ArrayList<>();
@ -420,6 +423,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
searchView.setEnabled(true);
updateScreenInsets(getResources().getConfiguration());
});
maybeShowUpgrade();
Timber.i("Finished app opening state!");
}
}, true);
@ -702,4 +706,60 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
super.onWindowFocusChanged(hasFocus);
this.updateScreenInsets();
}
public void maybeShowUpgrade() {
if (AndroidacyRepoData.getInstance() == null || AndroidacyRepoData.getInstance().memberLevel == null) {
// wait for up to 10 seconds for AndroidacyRepoData to be initialized
int i = 0;
while (AndroidacyRepoData.getInstance() == null && i < 10) {
try {
//noinspection BusyWait
Thread.sleep(1000);
} catch (InterruptedException e) {
Timber.e(e);
}
i++;
}
if (AndroidacyRepoData.getInstance().isEnabled() && AndroidacyRepoData.getInstance().memberLevel == null) {
Timber.d("Member level is null, waiting for it to be initialized");
i = 0;
while (AndroidacyRepoData.getInstance().memberLevel == null && i < 20) {
i++;
try {
//noinspection BusyWait
Thread.sleep(500);
} catch (InterruptedException e) {
Timber.e(e);
}
}
}
// if it's still null, but it's enabled, throw an error
if (AndroidacyRepoData.getInstance().isEnabled() && AndroidacyRepoData.getInstance().memberLevel == null) {
throw new IllegalStateException("AndroidacyRepoData is enabled, but member level is null");
}
if (AndroidacyRepoData.getInstance() != null && AndroidacyRepoData.getInstance().isEnabled() && Objects.equals(AndroidacyRepoData.getInstance().memberLevel, "Guest")) {
runtimeUtils.showUpgradeSnackbar(this, this);
} else {
if (!AndroidacyRepoData.getInstance().isEnabled()) {
Timber.i("AndroidacyRepoData is disabled, not showing upgrade snackbar 1");
} else if (!Objects.equals(AndroidacyRepoData.getInstance().memberLevel, "Guest")) {
Timber.i("AndroidacyRepoData is not Guest, not showing upgrade snackbar 1. Level: %s", AndroidacyRepoData.getInstance().memberLevel);
} else {
Timber.i("Unknown error, not showing upgrade snackbar 1");
}
}
} else if (AndroidacyRepoData.getInstance().isEnabled() && Objects.equals(AndroidacyRepoData.getInstance().memberLevel, "Guest")) {
runtimeUtils.showUpgradeSnackbar(this, this);
} else {
if (!AndroidacyRepoData.getInstance().isEnabled()) {
Timber.i("AndroidacyRepoData is disabled, not showing upgrade snackbar 2");
} else if (!Objects.equals(AndroidacyRepoData.getInstance().memberLevel, "Guest")) {
Timber.i("AndroidacyRepoData is not Guest, not showing upgrade snackbar 2. Level: %s", AndroidacyRepoData.getInstance().memberLevel);
} else {
Timber.i("Unknown error, not showing upgrade snackbar 2");
}
}
}
}

@ -224,7 +224,7 @@ public class UpdateActivity extends FoxActivity {
});
}
// convert to JSON
JSONObject latestJSON = new JSONObject(Arrays.toString(lastestJSON));
JSONObject latestJSON = new JSONObject(new String(lastestJSON));
String changelog = latestJSON.getString("body");
// set changelog text. changelog could be markdown, so we need to convert it to HTML
MaterialTextView changelogTextView = findViewById(R.id.update_changelog);

@ -275,12 +275,14 @@ public final class AndroidacyActivity extends FoxActivity {
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
switch (consoleMessage.messageLevel()) {
case TIP -> Timber.tag("JSLog").i(consoleMessage.message());
case LOG -> Timber.tag("JSLog").d(consoleMessage.message());
case WARNING -> Timber.tag("JSLog").w(consoleMessage.message());
case ERROR -> Timber.tag("JSLog").e(consoleMessage.message());
default -> Timber.tag("JSLog").v(consoleMessage.message());
if (BuildConfig.DEBUG_HTTP) {
switch (consoleMessage.messageLevel()) {
case TIP -> Timber.tag("JSLog").i(consoleMessage.message());
case LOG -> Timber.tag("JSLog").d(consoleMessage.message());
case WARNING -> Timber.tag("JSLog").w(consoleMessage.message());
case ERROR -> Timber.tag("JSLog").e(consoleMessage.message());
default -> Timber.tag("JSLog").v(consoleMessage.message());
}
}
return true;
}

@ -1,6 +1,8 @@
package com.fox2code.mmm.androidacy;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
@ -10,18 +12,23 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import com.fingerprintjs.android.fingerprint.Fingerprinter;
import com.fingerprintjs.android.fingerprint.FingerprinterFactory;
import com.fox2code.foxcompat.app.FoxActivity;
import com.fox2code.foxcompat.app.internal.FoxCompat;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.MainActivity;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R;
import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.repo.RepoData;
import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.repo.RepoModule;
import com.fox2code.mmm.utils.RuntimeUtils;
import com.fox2code.mmm.utils.io.PropUtils;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.net.HttpException;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.topjohnwu.superuser.Shell;
import org.json.JSONArray;
import org.json.JSONException;
@ -31,13 +38,13 @@ import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import okhttp3.HttpUrl;
import timber.log.Timber;
@ -94,59 +101,36 @@ public final class AndroidacyRepoData extends RepoData {
}
// Try to get the device ID from the shared preferences
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("androidacy");
String deviceIdPref = sharedPreferences.getString("device_id", null);
String deviceIdPref = sharedPreferences.getString("device_id_v2", null);
if (deviceIdPref != null) {
ANDROIDACY_DEVICE_ID = deviceIdPref;
return deviceIdPref;
} else {
// Really not that scary - just hashes some device info. We can't even get the info
// we originally hashed, so it's not like we can use it to track you.
String deviceId = null;
// Get ro.serialno if it exists
// First, we need to get an su shell
Shell.Result result = Shell.cmd("getprop ro.serialno").exec();
// Check if the command was successful
if (result.isSuccess()) {
// Get the output
String output = result.getOut().get(0);
// Check if the output is valid
if (output != null && !output.isEmpty()) {
deviceId = output;
Fingerprinter fp = FingerprinterFactory.create(MainApplication.getINSTANCE().getApplicationContext());
fp.getFingerprint(Fingerprinter.Version.V_5, fingerprint-> {
ANDROIDACY_DEVICE_ID = fingerprint;
// use fingerprint
// Save the device ID to the shared preferences
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("device_id_v2", ANDROIDACY_DEVICE_ID);
editor.apply();
return null;
});
// wait for up to 5 seconds for the fingerprint to be generated (ANDROIDACY_DEVICE_ID to be set)
long startTime = System.currentTimeMillis();
while (ANDROIDACY_DEVICE_ID == null && System.currentTimeMillis() - startTime < 5000) {
try {
//noinspection BusyWait
Thread.sleep(100);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
// Now, get device model, manufacturer, and Android version originally from
String deviceModel = android.os.Build.MODEL;
String deviceManufacturer = android.os.Build.MANUFACTURER;
String androidVersion = android.os.Build.VERSION.RELEASE;
// Append it all together
deviceId += deviceModel + deviceManufacturer + androidVersion;
// Hash it
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException ignored) {
// This should never happen so we can just return the original device ID
ANDROIDACY_DEVICE_ID = deviceId;
return deviceId;
}
byte[] hash = digest.digest(deviceId.getBytes());
// Convert it to a hex string
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
if (ANDROIDACY_DEVICE_ID == null) {
// fingerprint generation failed, use a random UUID
ANDROIDACY_DEVICE_ID = UUID.randomUUID().toString();
}
// Save it to shared preferences
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("device_id", hexString.toString());
editor.apply();
// Set ANDROIDACY_DEVICE_ID
ANDROIDACY_DEVICE_ID = hexString.toString();
// Return it
return hexString.toString();
return ANDROIDACY_DEVICE_ID;
}
}
@ -155,8 +139,9 @@ public final class AndroidacyRepoData extends RepoData {
try {
byte[] resp = Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId + "&client_id=" + BuildConfig.ANDROIDACY_CLIENT_ID, false);
// response is JSON
JSONObject jsonObject = new JSONObject(Arrays.toString(resp));
JSONObject jsonObject = new JSONObject(new String(resp));
memberLevel = jsonObject.getString("role");
Timber.d("Member level: %s", memberLevel);
JSONArray memberPermissions = jsonObject.getJSONArray("permissions");
// set role and permissions on userInfo property
userInfo = new String[][]{{"role", memberLevel}, {"permissions", String.valueOf(memberPermissions)}};
@ -252,7 +237,7 @@ public final class AndroidacyRepoData extends RepoData {
try {
Timber.i("Requesting new token...");
// POST json request to https://production-api.androidacy.com/auth/register
token = Arrays.toString(Http.doHttpPost("https://" + this.host + "/auth/register?client_id=" + BuildConfig.ANDROIDACY_CLIENT_ID, "{\"device_id\":\"" + deviceId + "\"}", false));
token = new String(Http.doHttpPost("https://" + this.host + "/auth/register?client_id=" + BuildConfig.ANDROIDACY_CLIENT_ID, "{\"device_id\":\"" + deviceId + "\"}", false));
// Parse token
try {
JSONObject jsonObject = new JSONObject(token);
@ -261,6 +246,7 @@ public final class AndroidacyRepoData extends RepoData {
//noinspection SuspiciousRegexArgument
Timber.d("Token: %s", token.substring(0, token.length() - 4).replaceAll(".", "*") + token.substring(token.length() - 4));
memberLevel = jsonObject.getString("role");
Timber.d("Member level: %s", memberLevel);
} catch (JSONException e) {
Timber.e(e, "Failed to parse token");
// Show a toast

@ -133,6 +133,6 @@ public enum AndroidacyUtil {
if (md == null) {
return null;
}
return Arrays.toString(md);
return new String(md);
}
}

@ -153,34 +153,28 @@ public class BackgroundUpdateChecker extends Worker {
}
RepoModule repoModule = repoModules.get(localModuleInfo.id);
localModuleInfo.checkModuleUpdate();
String remoteVersion = localModuleInfo.updateVersion;
String remoteVersionCode = String.valueOf(localModuleInfo.updateVersionCode);
if (repoModule != null) {
remoteVersion = repoModule.moduleInfo.version;
remoteVersionCode = String.valueOf(repoModule.moduleInfo.versionCode);
}
// now, coerce everything into an int
int localVersionCode = Integer.parseInt(String.valueOf(localModuleInfo.versionCode));
int remoteVersionCodeInt = Integer.parseInt(remoteVersionCode);
// we also have to match the version name
int localVersion = Integer.parseInt(localModuleInfo.version);
int remoteVersionInt = Integer.parseInt(remoteVersion);
int wantsVersion = Integer.parseInt(version.split(":")[1]);
int wantsVersion = Integer.parseInt(version.split(":")[1].replaceAll("[^0-9]", ""));
// now find out if user wants up to and including this version, or this version and newer
// if it starts with ^, it's this version and newer, if it ends with $, it's this version and older
if (version.startsWith("^")) {
// this version and newer
if (wantsVersion > localVersion || wantsVersion > remoteVersionInt || wantsVersion > remoteVersionCodeInt || wantsVersion < localVersionCode) {
if (wantsVersion <= remoteVersionCodeInt || wantsVersion <= localVersionCode) {
// if it is, we skip it
continue;
}
} else if (version.endsWith("$")) {
// this version and older
if (wantsVersion < localVersion || wantsVersion < remoteVersionInt || wantsVersion < remoteVersionCodeInt || wantsVersion > localVersionCode) {
if (wantsVersion >= remoteVersionCodeInt || wantsVersion >= localVersionCode) {
// if it is, we skip it
continue;
}
} else if (wantsVersion == localVersion || wantsVersion == remoteVersionInt || wantsVersion == remoteVersionCodeInt || wantsVersion == localVersionCode) {
} else if (wantsVersion == remoteVersionCodeInt || wantsVersion == localVersionCode) {
// if it is, we skip it
continue;
}

@ -152,34 +152,29 @@ public final class ModuleHolder implements Comparable<ModuleHolder> {
// get the one matching
version = stringSet.stream().filter(s -> s.startsWith(moduleInfo.id)).findFirst().orElse("");
}
String remoteVersion = moduleInfo.updateVersion;
String remoteVersionCode = String.valueOf(moduleInfo.updateVersionCode);
if (repoModule != null) {
remoteVersion = repoModule.moduleInfo.version;
remoteVersionCode = String.valueOf(repoModule.moduleInfo.versionCode);
}
// now, coerce everything into an int
int localVersionCode = Integer.parseInt(String.valueOf(moduleInfo.versionCode));
int remoteVersionCodeInt = Integer.parseInt(remoteVersionCode);
// we also have to match the version name
int localVersion = Integer.parseInt(moduleInfo.version);
int remoteVersionInt = Integer.parseInt(remoteVersion);
int wantsVersion = Integer.parseInt(version.split(":")[1]);
int wantsVersion = Integer.parseInt(version.split(":")[1].replaceAll("[^0-9]", ""));
// now find out if user wants up to and including this version, or this version and newer
// if it starts with ^, it's this version and newer, if it ends with $, it's this version and older
if (version.startsWith("^")) {
// this version and newer
if (wantsVersion > localVersion || wantsVersion > remoteVersionInt || wantsVersion > remoteVersionCodeInt || wantsVersion < localVersionCode) {
if (wantsVersion <= remoteVersionCodeInt || wantsVersion <= localVersionCode) {
// if it is, we skip it
ignoreUpdate = true;
}
} else if (version.endsWith("$")) {
// this version and older
if (wantsVersion < localVersion || wantsVersion < remoteVersionInt || wantsVersion < remoteVersionCodeInt || wantsVersion > localVersionCode) {
if (wantsVersion >= remoteVersionCodeInt || wantsVersion >= localVersionCode) {
// if it is, we skip it
ignoreUpdate = true;
}
} else if (wantsVersion == localVersion || wantsVersion == remoteVersionInt || wantsVersion == remoteVersionCodeInt || wantsVersion == localVersionCode) {
} else if (wantsVersion == remoteVersionCodeInt || wantsVersion == localVersionCode) {
// if it is, we skip it
ignoreUpdate = true;
}

@ -75,7 +75,7 @@ public class CustomRepoManager {
// parse json
JSONObject jsonObject;
try {
jsonObject = new JSONObject(Arrays.toString(json));
jsonObject = new JSONObject(new String(json));
} catch (Exception e) {
Timber.e(e, "Failed to parse json from repo");
return null;

@ -671,8 +671,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
EditText editText = (EditText) layout.getChildAt(i);
String text = editText.getText().toString();
if (!text.isEmpty()) {
// text cannot contain a colon because we use that to split id and version
text = text.replace(":", "");
// text can only contain numbers
text = text.replaceAll("[^0-9]", "");
// we have to use module id even though we show name
stringSetTemp.add(localModuleInfos.stream().filter(localModuleInfo -> localModuleInfo.name.equals(editText.getHint().toString())).findFirst().orElse(null).id + ":" + text);
Timber.d("text is %s for %s", text, editText.getHint().toString());
@ -942,23 +942,29 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// now handle pref_donate_androidacy
LongClickablePreference pref_donate_androidacy = findPreference("pref_donate_androidacy");
if (!BuildConfig.FLAVOR.equals("play")) {
pref_donate_androidacy.setOnPreferenceClickListener(p -> {
// copy FOX2CODE promo code to clipboard and toast user that they can use it for half off any subscription
String toastText = requireContext().getString(R.string.promo_code_copied);
clipboard.setPrimaryClip(ClipData.newPlainText(toastText, "FOX2CODE"));
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show();
// open androidacy
IntentHelper.openUrl(getFoxActivity(this), "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm_medium=app&utm_campaign=donate");
return true;
});
// handle long click on pref_donate_androidacy
pref_donate_androidacy.setOnPreferenceLongClickListener(p -> {
// copy to clipboard
String toastText = requireContext().getString(R.string.link_copied);
clipboard.setPrimaryClip(ClipData.newPlainText(toastText, "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm_medium=app&utm_campaign=donate"));
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show();
return true;
});
if (Objects.equals(AndroidacyRepoData.getInstance().memberLevel, "Guest")) {
pref_donate_androidacy.setOnPreferenceClickListener(p -> {
// copy FOX2CODE promo code to clipboard and toast user that they can use it for half off any subscription
String toastText = requireContext().getString(R.string.promo_code_copied);
clipboard.setPrimaryClip(ClipData.newPlainText(toastText, "FOX2CODE"));
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show();
// open androidacy
IntentHelper.openUrl(getFoxActivity(this), "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm_medium=app&utm_campaign=donate");
return true;
});
// handle long click on pref_donate_androidacy
pref_donate_androidacy.setOnPreferenceLongClickListener(p -> {
// copy to clipboard
String toastText = requireContext().getString(R.string.link_copied);
clipboard.setPrimaryClip(ClipData.newPlainText(toastText, "https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm_medium=app&utm_campaign=donate"));
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show();
return true;
});
} else {
// set text to "Thank you for your support!"
pref_donate_androidacy.setSummary(R.string.androidacy_thanks_up);
pref_donate_androidacy.setTitle(R.string.androidacy_thanks_up_title);
}
} else {
pref_donate_androidacy.setVisible(false);
}

@ -219,4 +219,35 @@ class RuntimeUtils {
snackbar.show()
prefs.edit().putInt("weblate_snackbar_shown", BuildConfig.VERSION_CODE).apply()
}
/**
* Shows a snackbar to upgrade androidacy membership.
* Sure it'll be wildly popular but it's only shown for 7 seconds every 7 days.
* We could y'know stick ads in the app but we're gonna play nice.
* @param context
* @param activity
*/
@SuppressLint("RestrictedApi")
fun showUpgradeSnackbar(context: Context, activity: MainActivity) {
Timber.i("showUpgradeSnackbar start")
val prefs = MainApplication.getSharedPreferences("mmm")
// if last shown < 7 days ago
if (prefs.getLong("ugsns4", 0) > System.currentTimeMillis() - 604800000) return
val snackbar: Snackbar = Snackbar.make(
context,
activity.findViewById(R.id.blur_frame),
activity.getString(R.string.upgrade_snackbar),
7000
)
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")
activity.startActivity(intent)
}
snackbar.setAnchorView(R.id.bottom_navigation)
snackbar.show()
// do not show for another 7 days
prefs.edit().putLong("ugsns4", System.currentTimeMillis()).apply()
Timber.i("showUpgradeSnackbar done")
}
}

@ -419,8 +419,12 @@
<string name="notification_update_ignore_version_desc">Ignore specific versions when checking for module updates. Disabling update checks per-module above overrides this setting!</string>
<string name="notification_update_ignore_version_pref">Exclude version(s) from update checks</string>
<string name="background_update_check_excludes_version">Exclude version(s) of modules from checks</string>
<string name="background_update_check_excludes_version_summary">Specify versions of modules you\'d like to ignore. Version code or version name will work, but version code may be more accurate. Use ^ at the beginning to match that version and newer. Use $ at the end to match up until that version.</string>
<string name="background_update_check_excludes_version_summary">Specify a version of module updates you\'d like to ignore. Please use the version code only. You can find this by holding down on the version in the module list. Use ^ at the beginning to match that version and newer. Use $ at the end to match up until that version.</string>
<string name="module_version">"Version: "</string>
<string name="module_remote_version">Remote version</string>
<string name="background_update_check_excludes_version_hint">Version(s) to ignore</string>
<string name="background_update_check_excludes_version_hint">Version code to ignore</string>
<string name="androidacy_thanks_up">Thanks for upgrading! Module downloads and READMEs will not show ads, and downloads will be unthrottled. In the future. you will be able to directly download modules with one click.</string>
<string name="androidacy_thanks_up_title">You\'ve already upgraded!</string>
<string name="upgrade_snackbar">Androidacy Premium offers faster downloads, an ad-free experience, and more!</string>
<string name="upgrade_now">Upgrade</string>
</resources>

Loading…
Cancel
Save