(misc) tweaks

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/299/head
androidacy-user 1 year ago
parent c4f1869331
commit ac845a0c0c

@ -191,6 +191,11 @@
android:resource="@xml/shared_file_paths" /> android:resource="@xml/shared_file_paths" />
</provider> </provider>
<service
android:name=".background.BackgroundUpdateCheckerService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
<meta-data <meta-data
android:name="io.sentry.auto-init" android:name="io.sentry.auto-init"
android:value="false" /> android:value="false" />

@ -52,6 +52,7 @@ import java.security.SecureRandom;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -89,6 +90,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
// Use FoxProcess wrapper helper. // Use FoxProcess wrapper helper.
private static final boolean wrapped = !FoxProcessExt.isRootLoader(); private static final boolean wrapped = !FoxProcessExt.isRootLoader();
private static boolean SHOWCASE_MODE_TRUE = false;
public static boolean isOfficial = false; public static boolean isOfficial = false;
private static long secret; private static long secret;
private static Locale timeFormatLocale = Resources.getSystem().getConfiguration().getLocales().get(0); private static Locale timeFormatLocale = Resources.getSystem().getConfiguration().getLocales().get(0);
@ -98,6 +100,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
private static MainApplication INSTANCE; private static MainApplication INSTANCE;
private static boolean firstBoot; private static boolean firstBoot;
private static HashMap<Object, Object> mSharedPrefs; private static HashMap<Object, Object> mSharedPrefs;
private static final ArrayList<String> callers = new ArrayList<>();
static { static {
Shell.setDefaultBuilder(shellBuilder = Shell.Builder.create().setFlags(Shell.FLAG_REDIRECT_STDERR).setTimeout(10).setInitializers(InstallerInitializer.class)); Shell.setDefaultBuilder(shellBuilder = Shell.Builder.create().setFlags(Shell.FLAG_REDIRECT_STDERR).setTimeout(10).setInitializers(InstallerInitializer.class));
@ -140,6 +143,24 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
Timber.d("Creating shared prefs map"); Timber.d("Creating shared prefs map");
mSharedPrefs = new HashMap<>(); mSharedPrefs = new HashMap<>();
} }
/*
this part is only here because with added encryption, parts of code that were previously calling this over and over again or on each invocation of a method are causing performance issues.
*/
if (BuildConfig.DEBUG) {
// get file, function, and line number
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// get the caller of this method
StackTraceElement caller = stackTrace[3];
Timber.d("Shared prefs file: %s, caller: %s:%d", name, caller.getMethodName(), caller.getLineNumber());
// add the caller to an array. if the last 3 callers are the same, then we are in a loop, log at error level
callers.add(name + ":" + caller.getLineNumber() + ":" + caller.getMethodName());
// get the last 3 callers
List<String> last3 = callers.subList(Math.max(callers.size() - 3, 0), callers.size());
// if the last 3 callers are the same, then we are in a loop, log at error level
if (last3.size() == 3 && last3.get(0).equals(last3.get(1)) && last3.get(1).equals(last3.get(2))) {
Timber.e("Shared prefs loop detected. File: %s, caller: %s:%d", name, caller.getMethodName(), caller.getLineNumber());
}
}
if (mSharedPrefs.containsKey(name)) { if (mSharedPrefs.containsKey(name)) {
Timber.d("Returning cached shared prefs"); Timber.d("Returning cached shared prefs");
return (SharedPreferences) mSharedPrefs.get(name); return (SharedPreferences) mSharedPrefs.get(name);
@ -167,7 +188,10 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
} }
public static boolean isShowcaseMode() { public static boolean isShowcaseMode() {
return getPreferences("mmm").getBoolean("pref_showcase_mode", false); if (SHOWCASE_MODE_TRUE) return true;
boolean showcaseMode = getPreferences("mmm").getBoolean("pref_showcase_mode", false);
SHOWCASE_MODE_TRUE = showcaseMode;
return showcaseMode;
} }
public static boolean shouldPreventReboot() { public static boolean shouldPreventReboot() {

@ -43,6 +43,7 @@ import timber.log.Timber;
@SuppressWarnings("KotlinInternalInJava") @SuppressWarnings("KotlinInternalInJava")
public final class AndroidacyRepoData extends RepoData { public final class AndroidacyRepoData extends RepoData {
public static String ANDROIDACY_DEVICE_ID = null;
public static String token = MainApplication.getPreferences("androidacy").getString("pref_androidacy_api_token", null); public static String token = MainApplication.getPreferences("androidacy").getString("pref_androidacy_api_token", null);
static { static {
@ -86,10 +87,15 @@ public final class AndroidacyRepoData extends RepoData {
// Generates a unique device ID. This is used to identify the device in the API for rate // Generates a unique device ID. This is used to identify the device in the API for rate
// limiting and fraud detection. // limiting and fraud detection.
public static String generateDeviceId() { public static String generateDeviceId() {
// first, check if ANDROIDACY_DEVICE_ID is already set
if (ANDROIDACY_DEVICE_ID != null) {
return ANDROIDACY_DEVICE_ID;
}
// Try to get the device ID from the shared preferences // Try to get the device ID from the shared preferences
SharedPreferences sharedPreferences = MainApplication.getPreferences("androidacy"); SharedPreferences sharedPreferences = MainApplication.getPreferences("androidacy");
String deviceIdPref = sharedPreferences.getString("device_id", null); String deviceIdPref = sharedPreferences.getString("device_id", null);
if (deviceIdPref != null) { if (deviceIdPref != null) {
ANDROIDACY_DEVICE_ID = deviceIdPref;
return deviceIdPref; return deviceIdPref;
} else { } else {
// Really not that scary - just hashes some device info. We can't even get the info // Really not that scary - just hashes some device info. We can't even get the info
@ -119,6 +125,7 @@ public final class AndroidacyRepoData extends RepoData {
digest = MessageDigest.getInstance("SHA-256"); digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException ignored) { } catch (NoSuchAlgorithmException ignored) {
// This should never happen so we can just return the original device ID // This should never happen so we can just return the original device ID
ANDROIDACY_DEVICE_ID = deviceId;
return deviceId; return deviceId;
} }
byte[] hash = digest.digest(deviceId.getBytes()); byte[] hash = digest.digest(deviceId.getBytes());
@ -135,6 +142,8 @@ public final class AndroidacyRepoData extends RepoData {
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("device_id", hexString.toString()); editor.putString("device_id", hexString.toString());
editor.apply(); editor.apply();
// Set ANDROIDACY_DEVICE_ID
ANDROIDACY_DEVICE_ID = hexString.toString();
// Return it // Return it
return hexString.toString(); return hexString.toString();
} }
@ -151,8 +160,7 @@ public final class AndroidacyRepoData extends RepoData {
// set role and permissions on userInfo property // set role and permissions on userInfo property
userInfo = new String[][]{{"role", memberLevel}, {"permissions", String.valueOf(memberPermissions)}}; userInfo = new String[][]{{"role", memberLevel}, {"permissions", String.valueOf(memberPermissions)}};
return true; return true;
} catch ( } catch (HttpException e) {
HttpException e) {
if (e.getErrorCode() == 401) { if (e.getErrorCode() == 401) {
Timber.w("Invalid token, resetting..."); Timber.w("Invalid token, resetting...");
// Remove saved preference // Remove saved preference
@ -162,8 +170,7 @@ public final class AndroidacyRepoData extends RepoData {
return false; return false;
} }
throw e; throw e;
} catch ( } catch (JSONException e) {
JSONException e) {
// response is not JSON // response is not JSON
Timber.w("Invalid token, resetting..."); Timber.w("Invalid token, resetting...");
Timber.w(e); Timber.w(e);
@ -185,8 +192,7 @@ public final class AndroidacyRepoData extends RepoData {
editor.apply(); editor.apply();
return false; return false;
} }
if (Http.needCaptchaAndroidacy()) if (Http.needCaptchaAndroidacy()) return false;
return false;
// Implementation details discussed on telegram // Implementation details discussed on telegram
// First, ping the server to check if it's alive // First, ping the server to check if it's alive
try { try {
@ -208,15 +214,13 @@ public final class AndroidacyRepoData extends RepoData {
} }
return false; return false;
} }
} catch ( } catch (Exception e) {
Exception e) {
Timber.e(e, "Failed to ping server"); Timber.e(e, "Failed to ping server");
return false; return false;
} }
String deviceId = generateDeviceId(); String deviceId = generateDeviceId();
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
if (this.androidacyBlockade > time) if (this.androidacyBlockade > time) return true; // fake it till you make it. Basically,
return true; // fake it till you make it. Basically,
// don't fail just because we're rate limited. API and web rate limits are different. // don't fail just because we're rate limited. API and web rate limits are different.
this.androidacyBlockade = time + 30_000L; this.androidacyBlockade = time + 30_000L;
try { try {
@ -234,8 +238,7 @@ public final class AndroidacyRepoData extends RepoData {
} else { } else {
Timber.i("Using validated cached token"); Timber.i("Using validated cached token");
} }
} catch ( } catch (IOException e) {
IOException e) {
if (HttpException.shouldTimeout(e)) { if (HttpException.shouldTimeout(e)) {
Timber.e(e, "We are being rate limited!"); Timber.e(e, "We are being rate limited!");
this.androidacyBlockade = time + 3_600_000L; this.androidacyBlockade = time + 3_600_000L;
@ -256,8 +259,7 @@ public final class AndroidacyRepoData extends RepoData {
//noinspection SuspiciousRegexArgument //noinspection SuspiciousRegexArgument
Timber.d("Token: %s", token.substring(0, token.length() - 4).replaceAll(".", "*") + token.substring(token.length() - 4)); Timber.d("Token: %s", token.substring(0, token.length() - 4).replaceAll(".", "*") + token.substring(token.length() - 4));
memberLevel = jsonObject.getString("role"); memberLevel = jsonObject.getString("role");
} catch ( } catch (JSONException e) {
JSONException e) {
Timber.e(e, "Failed to parse token"); Timber.e(e, "Failed to parse token");
// Show a toast // Show a toast
Looper mainLooper = Looper.getMainLooper(); Looper mainLooper = Looper.getMainLooper();
@ -280,8 +282,7 @@ public final class AndroidacyRepoData extends RepoData {
editor.apply(); editor.apply();
Timber.i("Token saved to shared preference"); Timber.i("Token saved to shared preference");
} }
} catch ( } catch (Exception e) {
Exception e) {
if (HttpException.shouldTimeout(e)) { if (HttpException.shouldTimeout(e)) {
Timber.e(e, "We are being rate limited!"); Timber.e(e, "We are being rate limited!");
this.androidacyBlockade = time + 3_600_000L; this.androidacyBlockade = time + 3_600_000L;
@ -384,8 +385,7 @@ public final class AndroidacyRepoData extends RepoData {
moduleInfo.minMagisk = // Allow 24.1 to mean 24100 moduleInfo.minMagisk = // Allow 24.1 to mean 24100
(Integer.parseInt(minMagisk.substring(0, c)) * 1000) + (Integer.parseInt(minMagisk.substring(c + 1)) * 100); (Integer.parseInt(minMagisk.substring(0, c)) * 1000) + (Integer.parseInt(minMagisk.substring(c + 1)) * 100);
} }
} catch ( } catch (Exception e) {
Exception e) {
moduleInfo.minMagisk = 0; moduleInfo.minMagisk = 0;
} }
moduleInfo.needRamdisk = jsonObject.optBoolean("needRamdisk", false); moduleInfo.needRamdisk = jsonObject.optBoolean("needRamdisk", false);
@ -437,8 +437,7 @@ public final class AndroidacyRepoData extends RepoData {
private String injectToken(String url) { private String injectToken(String url) {
// Do not inject token for non Androidacy urls // Do not inject token for non Androidacy urls
if (!AndroidacyUtil.isAndroidacyLink(url)) if (!AndroidacyUtil.isAndroidacyLink(url)) return url;
return url;
if (this.testMode) { if (this.testMode) {
if (url.startsWith("https://production-api.androidacy.com/")) { if (url.startsWith("https://production-api.androidacy.com/")) {
Timber.e("Got non test mode url: %s", AndroidacyUtil.hideToken(url)); Timber.e("Got non test mode url: %s", AndroidacyUtil.hideToken(url));

@ -51,10 +51,10 @@ public class BackgroundUpdateChecker extends Worker {
if (!MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check", false)) { if (!MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check", false)) {
return; return;
} }
if (MainApplication.getINSTANCE().isInForeground()) { //if (MainApplication.getINSTANCE().isInForeground()) {
// don't check if app is in foreground, this is a background check // don't check if app is in foreground, this is a background check
return; // return;
} //}
// next, check if user requires wifi // next, check if user requires wifi
if (MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check_wifi", true)) { if (MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check_wifi", true)) {
// check if wifi is connected // check if wifi is connected
@ -68,7 +68,7 @@ public class BackgroundUpdateChecker extends Worker {
// start foreground service // start foreground service
Intent intent = new Intent(context, BackgroundUpdateCheckerService.class); Intent intent = new Intent(context, BackgroundUpdateCheckerService.class);
intent.setAction(BackgroundUpdateCheckerService.ACTION_START_FOREGROUND_SERVICE); intent.setAction(BackgroundUpdateCheckerService.ACTION_START_FOREGROUND_SERVICE);
ContextCompat.startForegroundService(context, intent); context.startService(intent);
} }
public static void postNotification(Context context, HashMap<String, String> updateable, int updateCount, boolean test) { public static void postNotification(Context context, HashMap<String, String> updateable, int updateCount, boolean test) {

@ -6,16 +6,17 @@ import static com.fox2code.mmm.background.BackgroundUpdateChecker.postNotificati
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.IBinder;
import androidx.core.app.NotificationChannelCompat; import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.fox2code.foxcompat.app.internal.FoxIntentActivity;
import com.fox2code.mmm.AppUpdateManager; import com.fox2code.mmm.AppUpdateManager;
import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R; import com.fox2code.mmm.R;
@ -31,7 +32,7 @@ import java.util.HashMap;
import timber.log.Timber; import timber.log.Timber;
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
public class BackgroundUpdateCheckerService extends FoxIntentActivity { public class BackgroundUpdateCheckerService extends Service {
public static final String NOTIFICATION_CHANNEL_ID = "background_update"; public static final String NOTIFICATION_CHANNEL_ID = "background_update";
public static final String NOTIFICATION_CHANNEL_ID_APP = "background_update_app"; public static final String NOTIFICATION_CHANNEL_ID_APP = "background_update_app";
public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE"; public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE";
@ -68,83 +69,81 @@ public class BackgroundUpdateCheckerService extends FoxIntentActivity {
} }
} }
public void onCreate() { @Override
public IBinder onBind(Intent intent) {
Context context = MainApplication.getINSTANCE().getApplicationContext(); Context context = MainApplication.getINSTANCE().getApplicationContext();
// check if action is ACTION_START_FOREGROUND_SERVICE, bail out if not // check if ACTION_START_FOREGROUND_SERVICE was called
if (!ACTION_START_FOREGROUND_SERVICE.equals(getIntent().getAction())) { if (intent.getAction() != null && intent.getAction().equals(ACTION_START_FOREGROUND_SERVICE)) {
return; synchronized (lock) {
} // post checking notification if notifications are enabled
Timber.d("Starting background update checker service"); if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
// acquire lock NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
synchronized (lock) { notificationManager.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID_ONGOING, NotificationManagerCompat.IMPORTANCE_MIN).setName(context.getString(R.string.notification_channel_category_background_update)).setDescription(context.getString(R.string.notification_channel_category_background_update_description)).setGroup(NOTFIICATION_GROUP).build());
// post checking notification if notofiications are enabled NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { builder.setSmallIcon(R.drawable.ic_baseline_update_24);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); builder.setPriority(NotificationCompat.PRIORITY_MIN);
notificationManager.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID_ONGOING, NotificationManagerCompat.IMPORTANCE_MIN).setName(context.getString(R.string.notification_channel_category_background_update)).setDescription(context.getString(R.string.notification_channel_category_background_update_description)).setGroup(NOTFIICATION_GROUP).build()); builder.setCategory(NotificationCompat.CATEGORY_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); builder.setShowWhen(false);
builder.setSmallIcon(R.drawable.ic_baseline_update_24); builder.setOnlyAlertOnce(true);
builder.setPriority(NotificationCompat.PRIORITY_MIN); builder.setOngoing(true);
builder.setCategory(NotificationCompat.CATEGORY_SERVICE); builder.setAutoCancel(false);
builder.setShowWhen(false); builder.setGroup("update_bg");
builder.setOnlyAlertOnce(true); builder.setContentTitle(context.getString(R.string.notification_channel_background_update));
builder.setOngoing(true); builder.setContentText(context.getString(R.string.notification_channel_background_update_description));
builder.setAutoCancel(false); notificationManager.notify(NOTIFICATION_ID_ONGOING, builder.build());
builder.setGroup("update"); } else {
builder.setContentTitle(context.getString(R.string.notification_channel_background_update)); Timber.d("Not posting notification because of missing permission");
builder.setContentText(context.getString(R.string.notification_channel_background_update_description)); }
notificationManager.notify(NOTIFICATION_ID_ONGOING, builder.build()); ModuleManager.getINSTANCE().scanAsync();
} else { RepoManager.getINSTANCE().update(null);
Timber.d("Not posting notification because of missing permission"); ModuleManager.getINSTANCE().runAfterScan(() -> {
} int moduleUpdateCount = 0;
Thread.currentThread().setPriority(2); HashMap<String, RepoModule> repoModules = RepoManager.getINSTANCE().getModules();
ModuleManager.getINSTANCE().scanAsync(); // hashmap of updateable modules names
RepoManager.getINSTANCE().update(null); HashMap<String, String> updateableModules = new HashMap<>();
ModuleManager.getINSTANCE().runAfterScan(() -> { for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) {
int moduleUpdateCount = 0; if ("twrp-keep".equals(localModuleInfo.id)) continue;
HashMap<String, RepoModule> repoModules = RepoManager.getINSTANCE().getModules(); // exclude all modules with id's stored in the pref pref_background_update_check_excludes
// hasmap of updateable modules names try {
HashMap<String, String> updateableModules = new HashMap<>(); if (MainApplication.getPreferences("mmm").getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id))
for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) { continue;
if ("twrp-keep".equals(localModuleInfo.id)) continue; } catch (Exception ignored) {
// exclude all modules with id's stored in the pref pref_background_update_check_excludes }
try { RepoModule repoModule = repoModules.get(localModuleInfo.id);
if (MainApplication.getPreferences("mmm").getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id)) localModuleInfo.checkModuleUpdate();
continue; if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode && !PropUtils.isNullString(localModuleInfo.updateVersion)) {
} catch (Exception ignored) { moduleUpdateCount++;
updateableModules.put(localModuleInfo.name, localModuleInfo.version);
} else if (repoModule != null && repoModule.moduleInfo.versionCode > localModuleInfo.versionCode && !PropUtils.isNullString(repoModule.moduleInfo.version)) {
moduleUpdateCount++;
updateableModules.put(localModuleInfo.name, localModuleInfo.version);
}
} }
RepoModule repoModule = repoModules.get(localModuleInfo.id); if (moduleUpdateCount != 0) {
localModuleInfo.checkModuleUpdate(); Timber.d("Found %d updates", moduleUpdateCount);
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode && !PropUtils.isNullString(localModuleInfo.updateVersion)) { postNotification(context, updateableModules, moduleUpdateCount, false);
moduleUpdateCount++;
updateableModules.put(localModuleInfo.name, localModuleInfo.version);
} else if (repoModule != null && repoModule.moduleInfo.versionCode > localModuleInfo.versionCode && !PropUtils.isNullString(repoModule.moduleInfo.version)) {
moduleUpdateCount++;
updateableModules.put(localModuleInfo.name, localModuleInfo.version);
} }
} });
if (moduleUpdateCount != 0) { // check for app updates
Timber.d("Found %d updates", moduleUpdateCount); if (MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check_app", false)) {
postNotification(context, updateableModules, moduleUpdateCount, false); try {
} boolean shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true);
}); if (shouldUpdate) {
// check for app updates Timber.d("Found app update");
if (MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check_app", false)) { postNotificationForAppUpdate(context);
try { }
boolean shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true); } catch (Exception e) {
if (shouldUpdate) { e.printStackTrace();
Timber.d("Found app update");
postNotificationForAppUpdate(context);
} }
} catch (Exception e) {
e.printStackTrace();
} }
} // remove checking notification
// remove checking notification if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { Timber.d("Removing notification");
Timber.d("Removing notification"); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); notificationManager.cancel(NOTIFICATION_ID_ONGOING);
notificationManager.cancel(NOTIFICATION_ID_ONGOING); }
} }
} }
return null;
} }
} }

@ -361,6 +361,42 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
return true; return true;
}); });
// handle restart required for showcase mode
findPreference("pref_showcase_mode").setOnPreferenceChangeListener((p, v) -> {
if (v.equals(true)) {
new MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.restart).setMessage(R.string.showcase_mode_dialogue_message).setPositiveButton(R.string.ok, (dialog, which) -> {
// Toggle showcase mode on
((TwoStatePreference) findPreference("pref_showcase_mode")).setChecked(true);
editor.putBoolean("pref_showcase_mode", true).apply();
// restart app
Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent;
mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
Timber.d("Restarting app to save showcase mode preference: %s", v);
System.exit(0); // Exit app process
}).setNegativeButton(R.string.cancel, (dialog, which) -> {
// Revert to showcase mode on
((TwoStatePreference) findPreference("pref_showcase_mode")).setChecked(false);
editor.putBoolean("pref_showcase_mode", false).apply();
// restart app
Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent;
mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
Timber.d("Restarting app to save showcase mode preference: %s", v);
System.exit(0); // Exit app process
}).show();
}
return true;
});
Preference languageSelector = findPreference("pref_language_selector"); Preference languageSelector = findPreference("pref_language_selector");
languageSelector.setOnPreferenceClickListener(preference -> { languageSelector.setOnPreferenceClickListener(preference -> {
LanguageSwitcher ls = new LanguageSwitcher(getActivity()); LanguageSwitcher ls = new LanguageSwitcher(getActivity());

@ -399,4 +399,5 @@
<string name="language_not_available">Language %s has not been translated. Help translate it?</string> <string name="language_not_available">Language %s has not been translated. Help translate it?</string>
<string name="blur_desc">Creates a blur effect behind some dialogs and elements. Note that blur may not perform well on some devices and may not work for everyone.</string> <string name="blur_desc">Creates a blur effect behind some dialogs and elements. Note that blur may not perform well on some devices and may not work for everyone.</string>
<string name="error_encrypted_shared_preferences">An error occurred reading shared preferences. Please reset the app.</string> <string name="error_encrypted_shared_preferences">An error occurred reading shared preferences. Please reset the app.</string>
<string name="showcase_mode_dialogue_message">An app restart is required to enable showcase mode.</string>
</resources> </resources>

@ -156,7 +156,9 @@
app:singleLineTitle="false" app:singleLineTitle="false"
app:summary="@string/dns_over_https_desc" app:summary="@string/dns_over_https_desc"
app:title="@string/dns_over_https_pref" /> app:title="@string/dns_over_https_pref" />
<!-- Note: Lockdown mode used to be called showcase mode -->
<!-- TO DO: figure out why the f*** we need a showcase mode -->
<!-- like seriously, why? -->
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:icon="@drawable/ic_baseline_lock_24" app:icon="@drawable/ic_baseline_lock_24"

Loading…
Cancel
Save