Updating app widget using AlarmManager
Thanks for this example - I also had problems using a later Android version.
This post made it work for me: widget case that doesn't work (see the answer from Larry Schiefer).
So substituting for this from the code above:
Intent alarmIntent = new Intent(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
with this from the ref:
Intent alarmIntent=new Intent(mContext, MyWidget.class);
alarmIntent.setAction(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
did the job.
This is my solution, how to automatically update widget more frequently than the 30 minutes. I use AlarmManager. Before you use AlarmManager for refreshing appwidget, make sure you know what you do, because this technique could drain the device's battery.
Read more about widget update in Android doc - especially about updatePeriodMillis parameter.
This is part of my Manifest.xml. I define custom action AUTO_UPDATE.
<receiver android:name=".appwidget.AppWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="AUTO_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" />
</receiver>
This is part of my AppWidget.java. In onReceive method, I handle my custom action AUTO_UPDATE. In onEnabled and onDisabled methods, I start/stop alarm.
public class AppWidget extends AppWidgetProvider
{
public static final String ACTION_AUTO_UPDATE = "AUTO_UPDATE";
@Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if(intent.getAction().equals(ACTION_AUTO_UPDATE))
{
// DO SOMETHING
}
...
}
@Override
public void onEnabled(Context context)
{
// start alarm
AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
appWidgetAlarm.startAlarm();
}
@Override
public void onDisabled(Context context)
{
// stop alarm only if all widgets have been disabled
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisAppWidgetComponentName = new ComponentName(context.getPackageName(),getClass().getName());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidgetComponentName);
if (appWidgetIds.length == 0) {
// stop alarm
AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
appWidgetAlarm.stopAlarm();
}
}
...
}
This is my AppWidgetAlarm.java, which starts/stops alarm. Alarm manager sends broadcast to AppWidget.
public class AppWidgetAlarm
{
private final int ALARM_ID = 0;
private final int INTERVAL_MILLIS = 10000;
private Context mContext;
public AppWidgetAlarm(Context context)
{
mContext = context;
}
public void startAlarm()
{
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MILLISECOND, INTERVAL_MILLIS);
Intent alarmIntent=new Intent(mContext, AppWidget.class);
alarmIntent.setAction(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
// RTC does not wake the device up
alarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), INTERVAL_MILLIS, pendingIntent);
}
public void stopAlarm()
{
Intent alarmIntent = new Intent(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
}
A little bit modified version of petrnohejl's solution. This one is working in my project. (written in kotlin):
This is part of the Manifest.xml. I added the following actions: AUTO_UPDATE, APPWIDGET_UPDATE, APPWIDGET_ENABLED, APWIDGET_DISABLED.
<receiver android:name=".AppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="android.appwidget.action.APPWIDGET_DISABLED"/>
</intent-filter>
<intent-filter>
<action android:name="ACTION_AUTO_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info"/>
</receiver>
This is part of the AppWidget.kt. Here I implemented the onUpdate(), onEnabled(), onDisabled(), onReceive() functions.
class AppWidget: AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onEnabled(context: Context) { // Enter relevant functionality for when the first widget is created
// start alarm
val appWidgetAlarm = AppWidgetAlarm(context.applicationContext)
appWidgetAlarm.startAlarm()
}
override fun onDisabled(context: Context) { // Enter relevant functionality for when the last widget is disabled
// stop alarm only if all widgets have been disabled
val appWidgetManager = AppWidgetManager.getInstance(context)
if (appWidgetIds.isEmpty()) {
// stop alarm
val appWidgetAlarm = AppWidgetAlarm(context.getApplicationContext())
appWidgetAlarm.stopAlarm()
}
}
companion object {
val ACTION_AUTO_UPDATE = "AUTO_UPDATE"
fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
val widgetText = Random.nextInt(0, 100).toString()
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.appwidget)
views.setTextViewText(R.id.widget_text, widgetText)
// Instruct the widget manager to update the widget
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_text)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
// Do something
/*if (intent!!.action == ACTION_AUTO_UPDATE) {
// DO SOMETHING
}*/
}
}
And this is the AppWidgetAlarm.kt. Here it is my main modification. The answers didn't help me, but it is working. I set here a repeating alarm. (https://developer.android.com/training/scheduling/alarms)
class AppWidgetAlarm(private val context: Context?) {
private val ALARM_ID = 0
private val INTERVAL_MILLIS : Long = 10000
fun startAlarm() {
val calendar: Calendar = Calendar.getInstance()
calendar.add(Calendar.MILLISECOND, INTERVAL_MILLIS.toInt())
val alarmIntent = Intent(context, AppWidget::class.java).let { intent ->
//intent.action = AppWidget.ACTION_AUTO_UPDATE
PendingIntent.getBroadcast(context, 0, intent, 0)
}
with(context!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setRepeating(AlarmManager.RTC,calendar.timeInMillis, INTERVAL_MILLIS ,alarmIntent)
}
}
fun stopAlarm() {
val alarmIntent = Intent(AppWidget.ACTION_AUTO_UPDATE)
val pendingIntent = PendingIntent.getBroadcast(context, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT)
val alarmManager = context!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.cancel(pendingIntent)
}
}
I have an Update class that sets the AlarmManager:
No, you don't. AlarmManager
appears nowhere in the code snippet.
You do have a reference to AlarmManager
in the second code snippet. Problems there include:
You are setting a new repeating alarm every time the app widget updates
You are setting a 5 second frequency on the alarm, which is utter insanity
You are setting a 5 second frequency on a
_WAKEUP
alarm, which I think is grounds for your arrest in some jurisdictionsYou have a pointless
onReceive()
method, even ignoring the temporaryToast
You are assuming that there will be an action string on the
Intent
in yourToast
, but you do not specify an action string when you create theIntent
that you put in thePendingIntent
for the alarmYour code refers to what I presume are static data members on a
Battery
class, but it is rather likely those are all empty/null... or at least they would be, if you had a sane frequency on the alarm