package com.fox2code.mmm.background; import android.Manifest; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import androidx.annotation.NonNull; import androidx.core.app.NotificationChannelCompat; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; import androidx.work.Constraints; import androidx.work.ExistingPeriodicWorkPolicy; import androidx.work.NetworkType; import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; import androidx.work.Worker; import androidx.work.WorkerParameters; import com.fox2code.mmm.MainActivity; import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.R; import com.fox2code.mmm.manager.LocalModuleInfo; import com.fox2code.mmm.manager.ModuleManager; import com.fox2code.mmm.repo.RepoManager; import com.fox2code.mmm.repo.RepoModule; import com.fox2code.mmm.utils.io.PropUtils; import java.util.HashMap; import java.util.Random; import java.util.concurrent.TimeUnit; public class BackgroundUpdateChecker extends Worker { public static final String NOTIFICATION_CHANNEL_ID = "background_update"; public static final int NOTIFICATION_ID = 1; static final Object lock = new Object(); // Avoid concurrency issues private static boolean easterEggActive = false; public BackgroundUpdateChecker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } static void doCheck(Context context) { Thread.currentThread().setPriority(2); ModuleManager.getINSTANCE().scanAsync(); RepoManager.getINSTANCE().update(null); ModuleManager.getINSTANCE().runAfterScan(() -> { int moduleUpdateCount = 0; HashMap repoModules = RepoManager.getINSTANCE().getModules(); for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) { if ("twrp-keep".equals(localModuleInfo.id)) continue; // exclude all modules with id's stored in the pref pref_background_update_check_excludes if (MainApplication.getSharedPreferences().getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id)) continue; RepoModule repoModule = repoModules.get(localModuleInfo.id); localModuleInfo.checkModuleUpdate(); if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode && !PropUtils.isNullString(localModuleInfo.updateVersion)) { moduleUpdateCount++; } else if (repoModule != null && repoModule.moduleInfo.versionCode > localModuleInfo.versionCode && !PropUtils.isNullString(repoModule.moduleInfo.version)) { moduleUpdateCount++; } } if (moduleUpdateCount != 0) { postNotification(context, moduleUpdateCount, false); } }); } public static void postNotification(Context context, int updateCount, boolean test) { if (!easterEggActive) easterEggActive = new Random().nextInt(100) <= updateCount; NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID).setContentTitle(context.getString(easterEggActive ? R.string.notification_update_title_easter_egg : R.string.notification_update_title).replace("%i", String.valueOf(updateCount))).setContentText(context.getString(R.string.notification_update_subtitle)).setSmallIcon(R.drawable.ic_baseline_extension_24).setPriority(NotificationCompat.PRIORITY_HIGH).setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), PendingIntent.FLAG_IMMUTABLE)).setAutoCancel(true); if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { return; } // check if app is in foreground. if so, don't show notification if (MainApplication.getINSTANCE().isInForeground() && !test) return; NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build()); } public static void onMainActivityCreate(Context context) { // Refuse to run if first_launch pref is not false if (MainApplication.getSharedPreferences().getBoolean("first_time_setup_done", true)) return; NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context); notificationManagerCompat.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_HIGH).setShowBadge(true).setName(context.getString(R.string.notification_update_pref)).build()); notificationManagerCompat.cancel(BackgroundUpdateChecker.NOTIFICATION_ID); BackgroundUpdateChecker.easterEggActive = false; WorkManager.getInstance(context).enqueueUniquePeriodicWork("background_checker", ExistingPeriodicWorkPolicy.REPLACE, new PeriodicWorkRequest.Builder(BackgroundUpdateChecker.class, 6, TimeUnit.HOURS).setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).setRequiredNetworkType(NetworkType.UNMETERED).build()).build()); } public static void onMainActivityResume(Context context) { NotificationManagerCompat.from(context).cancel(BackgroundUpdateChecker.NOTIFICATION_ID); BackgroundUpdateChecker.easterEggActive = false; } @NonNull @Override public Result doWork() { if (!NotificationManagerCompat.from(this.getApplicationContext()).areNotificationsEnabled() || !MainApplication.isBackgroundUpdateCheckEnabled()) return Result.success(); synchronized (lock) { doCheck(this.getApplicationContext()); } return Result.success(); } }