Android widget bypass update interval via alarm manager for faster updates

This commit is contained in:
Andrei 2020-05-02 15:07:12 -07:00
parent 042e33d314
commit 48b30f9bf4
10 changed files with 145 additions and 38 deletions

View file

@ -152,13 +152,24 @@
<receiver android:name=".util.NotifyDismissReceiver" android:exported="false" />
<receiver android:name=".util.NotifySaveReceiver" android:exported="false" />
<receiver android:name=".util.NotifyMarkreadReceiver" android:exported="false" />
<receiver android:name=".widget.WidgetProvider" >
<receiver android:name=".widget.WidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/newsblur_appwidget_info" />
</receiver>
<receiver android:name=".service.TimeChangeReceiver">
<intent-filter >
<action android:name="android.intent.action.TIME_SET"/>
</intent-filter>
</receiver>
<receiver
android:name=".widget.WidgetUpdateReceiver"
android:enabled="true"
android:exported="false">
</receiver>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.newsblur.fileprovider"

View file

@ -8,10 +8,11 @@ import android.content.Context;
import android.content.Intent;
import com.newsblur.util.AppConstants;
import com.newsblur.widget.WidgetUtils;
/**
* First receiver in the chain that starts with the device. Simply schedules another broadcast
* that will periodicaly start the sync service.
* that will periodically start the sync service.
*/
public class BootReceiver extends BroadcastReceiver {
@ -19,6 +20,7 @@ public class BootReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
com.newsblur.util.Log.d(this, "triggering sync service from device boot");
scheduleSyncService(context);
resetWidgetSync(context);
}
public static void scheduleSyncService(Context context) {
@ -32,5 +34,9 @@ public class BootReceiver extends BroadcastReceiver {
int result = sched.schedule(builder.build());
com.newsblur.util.Log.d("BootReceiver", String.format("Scheduling result: %s - %s", result, result == 0 ? "Failure" : "Success"));
}
private static void resetWidgetSync(Context context) {
com.newsblur.util.Log.d(BootReceiver.class.getName(), "Received " + Intent.ACTION_BOOT_COMPLETED + " - reset widget sync");
WidgetUtils.resetUpdateAlarm(context);
}
}

View file

@ -0,0 +1,20 @@
package com.newsblur.service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.Nullable;
import com.newsblur.widget.WidgetUtils;
public class TimeChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, @Nullable Intent intent) {
if (intent != null && intent.getAction() != null
&& intent.getAction().equals(Intent.ACTION_TIME_CHANGED)) {
com.newsblur.util.Log.d(TimeChangeReceiver.class.getName(), "Received " + Intent.ACTION_TIME_CHANGED + " - reset widget sync");
WidgetUtils.resetUpdateAlarm(context);
}
}
}

View file

@ -6,14 +6,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.text.TextUtils;
import com.newsblur.R;
@ -28,7 +23,6 @@ import com.newsblur.fragment.ReadingActionConfirmationFragment;
import com.newsblur.network.APIManager;
import com.newsblur.network.domain.NewsBlurResponse;
import com.newsblur.service.NBSyncService;
import com.newsblur.widget.WidgetProvider;
public class FeedUtils {
@ -63,21 +57,6 @@ public class FeedUtils {
}
}
public static void triggerAppWidgetSync(Context context, int appWidgetId) {
// direct start service is not allowed when the app is in background
PersistableBundle bundle = new PersistableBundle();
bundle.putInt(WidgetProvider.EXTRA_WIDGET_ID, appWidgetId);
com.newsblur.util.Log.d(FeedUtils.class.getName(), "Trigger sync from background");
JobInfo.Builder builder = new JobInfo.Builder( 1, new ComponentName(context, NBSyncService.class));
builder.setOverrideDeadline(0);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setExtras(bundle);
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = scheduler.schedule(builder.build());
com.newsblur.util.Log.d(FeedUtils.class.getName(), String.format("Sync scheduling %s", result == 0 ? "failed" : "successful"));
}
public static void triggerSync(Context c) {
// NB: when our minSDKversion hits 28, it could be possible to start the service via the JobScheduler
// with the setImportantWhileForeground() flag via an enqueue() and get rid of all legacy startService

View file

@ -106,5 +106,6 @@ public class PrefConstants {
public static final String ENABLE_NOTIFICATIONS = "enable_notifications";
public static final String READING_FONT = "reading_font";
public static final String WIDGET_FEED_ID = "WIDGET_FEED_ID";
public static final String WIDGET_FEED_ID = "widget_feed_id";
public static final String WIDGET_ID = "widget_id";
}

View file

@ -878,8 +878,19 @@ public class PrefsUtils {
public static String getWidgetFeed(Context context, int widgetId) {
SharedPreferences preferences = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
String feedId = preferences.getString(PrefConstants.WIDGET_FEED_ID + widgetId, null);
return feedId;
return preferences.getString(PrefConstants.WIDGET_FEED_ID + widgetId, null);
}
public static void setWidgetId(Context context, int widgetId) {
SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
Editor editor = prefs.edit();
editor.putInt(PrefConstants.WIDGET_ID, widgetId);
editor.commit();
}
public static int getWidgetId(Context context) {
SharedPreferences preferences = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
return preferences.getInt(PrefConstants.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}
public static void removeWidgetFeed(Context context, int widgetId) {

View file

@ -16,20 +16,15 @@ import com.newsblur.util.FeedSet;
public class WidgetProvider extends AppWidgetProvider {
public static String ACTION_OPEN_STORY = "ACTION_OPEN_STORY";
public static String EXTRA_ITEM_ID = "EXTRA_ITEM_ID";
public static String EXTRA_FEED_ID = "EXTRA_FEED_ID";
public static String EXTRA_WIDGET_ID = "EXTRA_WIDGET_ID";
private static String TAG = "WidgetProvider";
// Called when the BroadcastReceiver receives an Intent broadcast.
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive");
if (intent.getAction().equals(ACTION_OPEN_STORY)) {
String storyHash = intent.getStringExtra(EXTRA_ITEM_ID);
String feedId = intent.getStringExtra(EXTRA_FEED_ID);
if (intent.getAction().equals(WidgetUtils.ACTION_OPEN_STORY)) {
String storyHash = intent.getStringExtra(WidgetUtils.EXTRA_ITEM_ID);
String feedId = intent.getStringExtra(WidgetUtils.EXTRA_FEED_ID);
FeedSet fs = FeedSet.singleFeed(feedId);
Intent i = new Intent(context, FeedReading.class);
i.putExtra(Reading.EXTRA_FEEDSET, fs);
@ -78,7 +73,7 @@ public class WidgetProvider extends AppWidgetProvider {
// Set the action for the intent.
// When the user touches a particular view, it will have the effect of
// broadcasting ACTION_OPEN_STORY.
touchIntent.setAction(WidgetProvider.ACTION_OPEN_STORY);
touchIntent.setAction(WidgetUtils.ACTION_OPEN_STORY);
touchIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent touchIntentTemplate = PendingIntent.getBroadcast(context, 0, touchIntent,

View file

@ -79,6 +79,8 @@ public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsF
FeedUtils.offerInitContext(context);
}
}
WidgetUtils.setUpdateAlarm(context, appWidgetId);
}
/**
@ -111,8 +113,8 @@ public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsF
// set fill-intent which is used to fill in the pending intent template
// set on the collection view in WidgetProvider
Bundle extras = new Bundle();
extras.putString(WidgetProvider.EXTRA_ITEM_ID, story.storyHash);
extras.putString(WidgetProvider.EXTRA_FEED_ID, story.feedId);
extras.putString(WidgetUtils.EXTRA_ITEM_ID, story.storyHash);
extras.putString(WidgetUtils.EXTRA_FEED_ID, story.feedId);
Intent fillInIntent = new Intent();
fillInIntent.putExtras(extras);
@ -185,6 +187,7 @@ public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsF
@Override
public void onDestroy() {
com.newsblur.util.Log.d(TAG, "onDestroy");
WidgetUtils.removeUpdateAlarm(context);
PrefsUtils.removeWidgetFeed(context, appWidgetId);
}

View file

@ -0,0 +1,26 @@
package com.newsblur.widget;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.Nullable;
import com.newsblur.R;
import com.newsblur.util.Log;
public class WidgetUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, @Nullable Intent intent) {
if (intent != null && intent.getAction() != null &&
intent.getAction().equals(WidgetUtils.ACTION_UPDATE_WIDGET)) {
Log.d(this.getClass().getName(), "Received " + WidgetUtils.ACTION_UPDATE_WIDGET);
int widgetId = intent.getIntExtra(WidgetUtils.EXTRA_WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
widgetManager.notifyAppWidgetViewDataChanged(widgetId, R.id.widget_list);
}
}
}
}

View file

@ -0,0 +1,55 @@
package com.newsblur.widget;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import com.newsblur.util.Log;
import com.newsblur.util.PrefsUtils;
public class WidgetUtils {
private static String TAG = "WidgetUtils";
public static String ACTION_UPDATE_WIDGET = "ACTION_UPDATE_WIDGET";
public static String ACTION_OPEN_STORY = "ACTION_OPEN_STORY";
public static String EXTRA_ITEM_ID = "EXTRA_ITEM_ID";
public static String EXTRA_FEED_ID = "EXTRA_FEED_ID";
public static String EXTRA_WIDGET_ID = "EXTRA_WIDGET_ID";
public static int RC_WIDGET_UPDATE = 1;
static void setUpdateAlarm(Context context, int appWidgetId) {
Log.d(TAG, "setUpdateAlarm");
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = getUpdateIntent(context);
intent.putExtra(EXTRA_WIDGET_ID, appWidgetId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, RC_WIDGET_UPDATE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
int widgetUpdateInterval = 1000 * 60 * 5;
long startAlarmAt = SystemClock.currentThreadTimeMillis() + widgetUpdateInterval;
alarmManager.setInexactRepeating(AlarmManager.RTC, startAlarmAt, widgetUpdateInterval, pendingIntent);
}
static void removeUpdateAlarm(Context context) {
Log.d(TAG, "removeUpdateAlarm");
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, RC_WIDGET_UPDATE, getUpdateIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.cancel(pendingIntent);
}
public static void resetUpdateAlarm(Context context) {
int widgetId = PrefsUtils.getWidgetId(context);
if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
WidgetUtils.setUpdateAlarm(context, widgetId);
}
}
private static Intent getUpdateIntent(Context context) {
Intent intent = new Intent(context, WidgetUpdateReceiver.class);
intent.setAction(ACTION_UPDATE_WIDGET);
return intent;
}
}