You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
15 KiB
Java
250 lines
15 KiB
Java
package com.fox2code.mmm.background;
|
|
|
|
import android.Manifest;
|
|
import android.annotation.SuppressLint;
|
|
import android.app.NotificationChannelGroup;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.Network;
|
|
import android.net.NetworkCapabilities;
|
|
import android.os.Build;
|
|
|
|
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.PeriodicWorkRequest;
|
|
import androidx.work.WorkManager;
|
|
import androidx.work.Worker;
|
|
import androidx.work.WorkerParameters;
|
|
|
|
import com.fox2code.mmm.AppUpdateManager;
|
|
import com.fox2code.mmm.MainActivity;
|
|
import com.fox2code.mmm.MainApplication;
|
|
import com.fox2code.mmm.R;
|
|
import com.fox2code.mmm.UpdateActivity;
|
|
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.Map;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import timber.log.Timber;
|
|
|
|
@SuppressWarnings("SpellCheckingInspection")
|
|
public class BackgroundUpdateChecker extends Worker {
|
|
public static final String NOTIFICATION_CHANNEL_ID = "background_update";
|
|
public static final int NOTIFICATION_ID = 1;
|
|
public static final String NOTFIICATION_GROUP = "updates";
|
|
public static final String NOTIFICATION_CHANNEL_ID_APP = "background_update_app";
|
|
static final Object lock = new Object(); // Avoid concurrency issuespublic static final String NOTIFICATION_CHANNEL_ID = "background_update";
|
|
private static final int NOTIFICATION_ID_ONGOING = 2;
|
|
private static final String NOTIFICATION_CHANNEL_ID_ONGOING = "mmm_background_update";
|
|
private static final int NOTIFICATION_ID_APP = 3;
|
|
|
|
public BackgroundUpdateChecker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
|
super(context, workerParams);
|
|
}
|
|
|
|
@SuppressLint("RestrictedApi")
|
|
private static void postNotificationForAppUpdate(Context context) {
|
|
// create the notification channel if not already created
|
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
|
notificationManager.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID_APP, NotificationManagerCompat.IMPORTANCE_HIGH).setName(context.getString(R.string.notification_channel_category_app_update)).setDescription(context.getString(R.string.notification_channel_category_app_update_description)).setGroup(NOTFIICATION_GROUP).build());
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID_APP);
|
|
builder.setSmallIcon(R.drawable.baseline_system_update_24);
|
|
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
|
builder.setCategory(NotificationCompat.CATEGORY_RECOMMENDATION);
|
|
builder.setShowWhen(false);
|
|
builder.setOnlyAlertOnce(true);
|
|
builder.setOngoing(false);
|
|
builder.setAutoCancel(true);
|
|
builder.setGroup(NOTFIICATION_GROUP);
|
|
// open app on click
|
|
Intent intent = new Intent(context, UpdateActivity.class);
|
|
// set action to ACTIONS.DOWNLOAD
|
|
intent.setAction(String.valueOf(UpdateActivity.ACTIONS.DOWNLOAD));
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
builder.setContentIntent(android.app.PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE));
|
|
// set summary to Found X
|
|
builder.setContentTitle(context.getString(R.string.notification_channel_background_update_app));
|
|
builder.setContentText(context.getString(R.string.notification_channel_background_update_app_description));
|
|
if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
|
notificationManager.notify(NOTIFICATION_ID_APP, builder.build());
|
|
}
|
|
}
|
|
|
|
static void doCheck(Context context) {
|
|
// first, check if the user has enabled background update checking
|
|
if (!MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check", false)) {
|
|
return;
|
|
}
|
|
if (MainApplication.getINSTANCE().isInForeground()) {
|
|
// don't check if app is in foreground, this is a background check
|
|
return;
|
|
}
|
|
// next, check if user requires wifi
|
|
if (MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check_wifi", true)) {
|
|
// check if wifi is connected
|
|
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
Network networkInfo = connectivityManager.getActiveNetwork();
|
|
if (networkInfo == null || !connectivityManager.getNetworkCapabilities(networkInfo).hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
|
|
Timber.w("Background update check: wifi not connected but required");
|
|
return;
|
|
}
|
|
}
|
|
synchronized (lock) {
|
|
// post checking notification if notifications are enabled
|
|
if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
|
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());
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID_ONGOING);
|
|
builder.setSmallIcon(R.drawable.ic_baseline_update_24);
|
|
builder.setPriority(NotificationCompat.PRIORITY_MIN);
|
|
builder.setCategory(NotificationCompat.CATEGORY_SERVICE);
|
|
builder.setShowWhen(false);
|
|
builder.setOnlyAlertOnce(true);
|
|
builder.setOngoing(true);
|
|
builder.setAutoCancel(false);
|
|
builder.setGroup("update_bg");
|
|
builder.setContentTitle(context.getString(R.string.notification_channel_background_update));
|
|
builder.setContentText(context.getString(R.string.notification_channel_background_update_description));
|
|
notificationManager.notify(NOTIFICATION_ID_ONGOING, builder.build());
|
|
} else {
|
|
Timber.d("Not posting notification because of missing permission");
|
|
}
|
|
ModuleManager.getINSTANCE().scanAsync();
|
|
RepoManager.getINSTANCE().update(null);
|
|
ModuleManager.getINSTANCE().runAfterScan(() -> {
|
|
int moduleUpdateCount = 0;
|
|
HashMap<String, RepoModule> repoModules = RepoManager.getINSTANCE().getModules();
|
|
// hashmap of updateable modules names
|
|
HashMap<String, String> updateableModules = new HashMap<>();
|
|
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
|
|
try {
|
|
if (MainApplication.getSharedPreferences("mmm").getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id))
|
|
continue;
|
|
} catch (Exception ignored) {
|
|
}
|
|
RepoModule repoModule = repoModules.get(localModuleInfo.id);
|
|
localModuleInfo.checkModuleUpdate();
|
|
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode && !PropUtils.isNullString(localModuleInfo.updateVersion)) {
|
|
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) {
|
|
Timber.d("Found %d updates", moduleUpdateCount);
|
|
postNotification(context, updateableModules, moduleUpdateCount, false);
|
|
}
|
|
});
|
|
// check for app updates
|
|
if (MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check_app", false)) {
|
|
try {
|
|
boolean shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true);
|
|
if (shouldUpdate) {
|
|
Timber.d("Found app update");
|
|
postNotificationForAppUpdate(context);
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
// remove checking notification
|
|
if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
|
Timber.d("Removing notification");
|
|
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
|
notificationManager.cancel(NOTIFICATION_ID_ONGOING);
|
|
}
|
|
}
|
|
// increment or create counter in shared preferences
|
|
MainApplication.getSharedPreferences("mmm").edit().putInt("pref_background_update_counter", MainApplication.getSharedPreferences("mmm").getInt("pref_background_update_counter", 0) + 1).apply();
|
|
}
|
|
|
|
public static void postNotification(Context context, HashMap<String, String> updateable, int updateCount, boolean test) {
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
|
builder.setSmallIcon(R.drawable.baseline_system_update_24);
|
|
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
|
builder.setCategory(NotificationCompat.CATEGORY_RECOMMENDATION);
|
|
builder.setShowWhen(false);
|
|
builder.setOnlyAlertOnce(true);
|
|
builder.setOngoing(false);
|
|
builder.setAutoCancel(true);
|
|
builder.setGroup(NOTFIICATION_GROUP);
|
|
// open app on click
|
|
Intent intent = new Intent(context, MainActivity.class);
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
|
builder.setContentIntent(android.app.PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE));
|
|
// set summary to Found X updates: <module name> <module version> <module name> <module version> ...
|
|
StringBuilder summary = new StringBuilder();
|
|
summary.append(context.getString(R.string.notification_update_summary));
|
|
// use notification_update_module_template string to set name and version
|
|
for (Map.Entry<String, String> entry : updateable.entrySet()) {
|
|
summary.append("\n").append(context.getString(R.string.notification_update_module_template, entry.getKey(), entry.getValue()));
|
|
}
|
|
builder.setContentTitle(context.getString(R.string.notification_update_title, updateCount));
|
|
builder.setContentText(summary);
|
|
// set long text to summary so it doesn't get cut off
|
|
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(summary));
|
|
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 (!Objects.equals(MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", null), "v1"))
|
|
return;
|
|
// create notification channel group
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
CharSequence groupName = context.getString(R.string.notification_group_updates);
|
|
NotificationManager mNotificationManager = ContextCompat.getSystemService(context, NotificationManager.class);
|
|
Objects.requireNonNull(mNotificationManager).createNotificationChannelGroup(new NotificationChannelGroup(NOTFIICATION_GROUP, groupName));
|
|
}
|
|
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)).setDescription(context.getString(R.string.auto_updates_notifs)).setGroup(NOTFIICATION_GROUP).build());
|
|
notificationManagerCompat.cancel(BackgroundUpdateChecker.NOTIFICATION_ID);
|
|
// now for the ongoing notification
|
|
notificationManagerCompat.createNotificationChannel(new NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL_ID_ONGOING, NotificationManagerCompat.IMPORTANCE_MIN).setShowBadge(true).setName(context.getString(R.string.notification_update_pref)).setDescription(context.getString(R.string.auto_updates_notifs)).setGroup(NOTFIICATION_GROUP).build());
|
|
notificationManagerCompat.cancel(BackgroundUpdateChecker.NOTIFICATION_ID_ONGOING);
|
|
// schedule periodic check for updates every 6 hours (6 * 60 * 60 = 21600)
|
|
Timber.d("Scheduling periodic background check");
|
|
WorkManager.getInstance(context).enqueueUniquePeriodicWork("background_checker", ExistingPeriodicWorkPolicy.UPDATE, new PeriodicWorkRequest.Builder(BackgroundUpdateChecker.class, 6, TimeUnit.HOURS).setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()).build());
|
|
}
|
|
|
|
public static void onMainActivityResume(Context context) {
|
|
NotificationManagerCompat.from(context).cancel(BackgroundUpdateChecker.NOTIFICATION_ID);
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public Result doWork() {
|
|
if (!NotificationManagerCompat.from(this.getApplicationContext()).areNotificationsEnabled() || !MainApplication.isBackgroundUpdateCheckEnabled())
|
|
return Result.success();
|
|
synchronized (lock) {
|
|
doCheck(this.getApplicationContext());
|
|
}
|
|
return Result.success();
|
|
}
|
|
}
|