From 2034119b7196540d1a2a02a770ccdfb3171aea91 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Tue, 23 Aug 2022 10:06:30 +0200 Subject: [PATCH] NearestFavoriteStationWidgetService: migrate periodic refresh from JobIntentService to JobScheduler * Receive MY_PACKAGE_REPLACED and MY_PACKAGE_UNSUSPENDED system broadcasts to schedule our job. * Remove the foreground notification, as it's not necessary any more. --- oeffi/AndroidManifest.xml | 6 ++ .../ic_stat_notify_sync_24dp.xml | 11 --- .../xml/nearest_favorite_station_widget.xml | 3 +- .../src/de/schildbach/oeffi/Application.java | 21 ----- .../stations/FavoriteStationsActivity.java | 2 +- .../oeffi/stations/FavoriteUtils.java | 17 ---- .../NearestFavoriteStationWidgetProvider.java | 12 +-- .../NearestFavoriteStationWidgetService.java | 88 ++++++++++++++----- ...oriteStationsWidgetPermissionActivity.java | 2 +- .../stations/StationDetailsActivity.java | 4 +- .../oeffi/stations/StationsActivity.java | 8 +- 11 files changed, 81 insertions(+), 93 deletions(-) delete mode 100644 oeffi/res/drawable-anydpi/ic_stat_notify_sync_24dp.xml diff --git a/oeffi/AndroidManifest.xml b/oeffi/AndroidManifest.xml index a2e6313..a213549 100644 --- a/oeffi/AndroidManifest.xml +++ b/oeffi/AndroidManifest.xml @@ -248,6 +248,12 @@ + + + + + + diff --git a/oeffi/res/drawable-anydpi/ic_stat_notify_sync_24dp.xml b/oeffi/res/drawable-anydpi/ic_stat_notify_sync_24dp.xml deleted file mode 100644 index 9e6222a..0000000 --- a/oeffi/res/drawable-anydpi/ic_stat_notify_sync_24dp.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/oeffi/res/xml/nearest_favorite_station_widget.xml b/oeffi/res/xml/nearest_favorite_station_widget.xml index 157e072..98b8c3c 100644 --- a/oeffi/res/xml/nearest_favorite_station_widget.xml +++ b/oeffi/res/xml/nearest_favorite_station_widget.xml @@ -6,5 +6,4 @@ android:minResizeHeight="55dp" android:minWidth="294dp" android:previewImage="@drawable/nearest_favorite_station_widget_preview" - android:resizeMode="horizontal|vertical" - android:updatePeriodMillis="1800000" /> + android:resizeMode="horizontal|vertical" /> diff --git a/oeffi/src/de/schildbach/oeffi/Application.java b/oeffi/src/de/schildbach/oeffi/Application.java index a69fcab..9493840 100644 --- a/oeffi/src/de/schildbach/oeffi/Application.java +++ b/oeffi/src/de/schildbach/oeffi/Application.java @@ -17,13 +17,9 @@ package de.schildbach.oeffi; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Build; import android.preference.PreferenceManager; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; @@ -35,7 +31,6 @@ import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; import com.google.common.base.Stopwatch; import de.schildbach.oeffi.directions.QueryHistoryProvider; import de.schildbach.oeffi.stations.FavoriteStationsProvider; -import de.schildbach.oeffi.stations.NearestFavoriteStationWidgetService; import de.schildbach.oeffi.util.ErrorReporter; import de.schildbach.pte.NetworkId; import okhttp3.OkHttpClient; @@ -121,8 +116,6 @@ public class Application extends android.app.Application { QueryHistoryProvider.deleteQueryHistory(this, SBB); log.info("Migrations took {}", watch); - - initNotificationManager(); } private void initLogging() { @@ -178,20 +171,6 @@ public class Application extends android.app.Application { config.setUserAgentValue(getPackageName()); } - private void initNotificationManager() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - final Stopwatch watch = Stopwatch.createStarted(); - final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - final NotificationChannel appwidget = new NotificationChannel( - NearestFavoriteStationWidgetService.NOTIFICATION_CHANNEL_ID_APPWIDGET, - getString(R.string.notification_channel_appwidget_name), NotificationManager.IMPORTANCE_LOW); - nm.createNotificationChannel(appwidget); - - log.info("created notification channels, took {}", watch); - } - } - private void migrateSelectedNetwork(final String fromName, final NetworkId to) { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); diff --git a/oeffi/src/de/schildbach/oeffi/stations/FavoriteStationsActivity.java b/oeffi/src/de/schildbach/oeffi/stations/FavoriteStationsActivity.java index 0463554..7151c3d 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/FavoriteStationsActivity.java +++ b/oeffi/src/de/schildbach/oeffi/stations/FavoriteStationsActivity.java @@ -121,7 +121,7 @@ public class FavoriteStationsActivity extends OeffiActivity } else if (menuItemId == R.id.station_context_remove_favorite) { adapter.removeEntry(adapterPosition); updateGUI(); - FavoriteUtils.notifyFavoritesChanged(this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget return true; } else { return false; diff --git a/oeffi/src/de/schildbach/oeffi/stations/FavoriteUtils.java b/oeffi/src/de/schildbach/oeffi/stations/FavoriteUtils.java index 489c5f3..4a15681 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/FavoriteUtils.java +++ b/oeffi/src/de/schildbach/oeffi/stations/FavoriteUtils.java @@ -17,12 +17,8 @@ package de.schildbach.oeffi.stations; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; import android.content.ContentResolver; import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; import android.database.Cursor; import android.net.Uri; import de.schildbach.pte.NetworkId; @@ -71,17 +67,4 @@ public class FavoriteUtils { return favorites; } - public static void notifyFavoritesChanged(final Context context) { - // notify widgets - final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - for (final AppWidgetProviderInfo providerInfo : appWidgetManager.getInstalledProviders()) { - // limit to own widgets - if (providerInfo.provider.getPackageName().equals(context.getPackageName())) { - final Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, - appWidgetManager.getAppWidgetIds(providerInfo.provider)); - context.sendBroadcast(intent); - } - } - } } diff --git a/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetProvider.java b/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetProvider.java index 249370a..9c2ddfa 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetProvider.java +++ b/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetProvider.java @@ -17,7 +17,6 @@ package de.schildbach.oeffi.stations; -import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; import android.content.Intent; @@ -32,15 +31,6 @@ public class NearestFavoriteStationWidgetProvider extends AppWidgetProvider { public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); log.info("got broadcast: {}", action); - - if (Intent.ACTION_BOOT_COMPLETED.equals(action)) - NearestFavoriteStationWidgetService.enqueueWork(context, new Intent()); - else - super.onReceive(context, intent); - } - - @Override - public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds) { - NearestFavoriteStationWidgetService.enqueueWork(context, new Intent()); + NearestFavoriteStationWidgetService.schedulePeriodic(context); } } diff --git a/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetService.java b/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetService.java index 5db1406..1add583 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetService.java +++ b/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationWidgetService.java @@ -19,6 +19,10 @@ package de.schildbach.oeffi.stations; import android.Manifest; import android.app.PendingIntent; +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentResolver; @@ -38,10 +42,10 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Process; import android.text.format.DateFormat; +import android.text.format.DateUtils; import android.view.View; import android.widget.RemoteViews; -import androidx.core.app.JobIntentService; -import androidx.core.app.NotificationCompat; +import androidx.annotation.WorkerThread; import androidx.core.content.ContextCompat; import com.google.common.base.Throwables; import com.google.common.util.concurrent.SettableFuture; @@ -68,24 +72,59 @@ import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -public class NearestFavoriteStationWidgetService extends JobIntentService { +public class NearestFavoriteStationWidgetService extends JobService { private AppWidgetManager appWidgetManager; private LocationManager locationManager; private ContentResolver contentResolver; + private Executor executor = Executors.newFixedThreadPool(2); private HandlerThread backgroundThread; private Handler backgroundHandler; - private static final int JOB_ID = 1; - public static final String NOTIFICATION_CHANNEL_ID_APPWIDGET = "appwidget"; - private static final int NOTIFICATION_ID_APPWIDGET_UPDATE = 1; + private static final int JOB_ID_PERIODIC = 0; + private static final int JOB_ID_IMMEDIATE = 1; private static final Logger log = LoggerFactory.getLogger(NearestFavoriteStationWidgetService.class); - public static void enqueueWork(final Context context, final Intent work) { - enqueueWork(context, NearestFavoriteStationWidgetService.class, JOB_ID, work); + public static void schedulePeriodic(final Context context) { + final JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + final ComponentName providerName = new ComponentName(context, NearestFavoriteStationWidgetProvider.class); + final boolean haveWidgets = AppWidgetManager.getInstance(context).getAppWidgetIds(providerName).length > 0; + if (haveWidgets) { + final JobInfo.Builder jobInfo = new JobInfo.Builder(JOB_ID_PERIODIC, new ComponentName(context, + NearestFavoriteStationWidgetService.class)); + jobInfo.setPeriodic(DateUtils.MINUTE_IN_MILLIS * 15); + jobInfo.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + final JobInfo job = jobInfo.build(); + jobScheduler.schedule(job); + log.info("Scheduled periodic job: {}", job); + } else { + jobScheduler.cancelAll(); + } + } + + public static void scheduleImmediate(final Context context) { + final JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + final ComponentName providerName = new ComponentName(context, NearestFavoriteStationWidgetProvider.class); + final boolean haveWidgets = AppWidgetManager.getInstance(context).getAppWidgetIds(providerName).length > 0; + if (haveWidgets) { + final JobInfo.Builder jobInfo = new JobInfo.Builder(JOB_ID_IMMEDIATE, new ComponentName(context, + NearestFavoriteStationWidgetService.class)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + jobInfo.setExpedited(true); + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) + jobInfo.setImportantWhileForeground(true); + jobInfo.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + final JobInfo job = jobInfo.build(); + jobScheduler.schedule(job); + log.info("Scheduled immediate job: {}", job); + } else { + jobScheduler.cancelAll(); + } } @Override @@ -110,25 +149,28 @@ public class NearestFavoriteStationWidgetService extends JobIntentService { private RemoteViews views; @Override - protected void onHandleWork(final Intent intent) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - final NotificationCompat.Builder notification = new NotificationCompat.Builder(this, - NOTIFICATION_CHANNEL_ID_APPWIDGET); - notification.setSmallIcon(R.drawable.ic_stat_notify_sync_24dp); - notification.setWhen(System.currentTimeMillis()); - notification.setOngoing(true); - startForeground(NOTIFICATION_ID_APPWIDGET_UPDATE, notification.build()); - } - - handleIntent(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - stopForeground(true); + public boolean onStartJob(final JobParameters params) { + log.info("Job started: {}", params); + executor.execute(() -> { + runJob(); + jobFinished(params, false); + log.info("Job finished: {}", params); + }); + return true; } - private void handleIntent() { + @Override + public boolean onStopJob(final JobParameters params) { + log.info("Job stopped: {}", params); + return false; + } + + @WorkerThread + private void runJob() { final ComponentName providerName = new ComponentName(this, NearestFavoriteStationWidgetProvider.class); final int[] appWidgetIds = appWidgetManager.getAppWidgetIds(providerName); + if (appWidgetIds.length == 0) + return; views = new RemoteViews(getPackageName(), R.layout.station_widget_content); diff --git a/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationsWidgetPermissionActivity.java b/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationsWidgetPermissionActivity.java index a32d0d6..7bc4648 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationsWidgetPermissionActivity.java +++ b/oeffi/src/de/schildbach/oeffi/stations/NearestFavoriteStationsWidgetPermissionActivity.java @@ -53,7 +53,7 @@ public class NearestFavoriteStationsWidgetPermissionActivity extends Activity { for (int i = 0; i < permissions.length; i++) log.info("{}{} granted", permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED ? "" : " " + "not"); - FavoriteUtils.notifyFavoritesChanged(this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget finish(); } } diff --git a/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java b/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java index c1d3c76..7bd2bce 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java +++ b/oeffi/src/de/schildbach/oeffi/stations/StationDetailsActivity.java @@ -157,13 +157,13 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa FavoriteStationsProvider.TYPE_FAVORITE, selectedNetwork, selectedStation); if (rowUri != null) { selectedFavState = FavoriteStationsProvider.TYPE_FAVORITE; - FavoriteUtils.notifyFavoritesChanged(StationDetailsActivity.this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget } } else { final int numRows = FavoriteUtils.delete(getContentResolver(), selectedNetwork, selectedStation.id); if (numRows > 0) { selectedFavState = null; - FavoriteUtils.notifyFavoritesChanged(StationDetailsActivity.this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget } } }); diff --git a/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java b/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java index 45f4908..9ffdf9e 100644 --- a/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java +++ b/oeffi/src/de/schildbach/oeffi/stations/StationsActivity.java @@ -696,7 +696,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware if (rowUri != null) { favorites.put(location.id, FavoriteStationsProvider.TYPE_FAVORITE); postLoadNextVisible(0); - FavoriteUtils.notifyFavoritesChanged(this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget return true; } else { return false; @@ -707,7 +707,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware final int numRows = FavoriteUtils.delete(getContentResolver(), network, location.id); if (numRows > 0) { favorites.remove(location.id); - FavoriteUtils.notifyFavoritesChanged(this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget return true; } else { return false; @@ -719,7 +719,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware network, location); if (rowUriIgnored != null) { favorites.put(location.id, FavoriteStationsProvider.TYPE_IGNORE); - FavoriteUtils.notifyFavoritesChanged(this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget return true; } else { return false; @@ -731,7 +731,7 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware if (numRowsIgnored > 0) { favorites.remove(location.id); postLoadNextVisible(0); - FavoriteUtils.notifyFavoritesChanged(this); + NearestFavoriteStationWidgetService.scheduleImmediate(this); // refresh app-widget return true; } else { return false;