I have a widgetProvider . I also have a service class that does the calculation for the widget.
The widget runs fine when initialized. I'm getting an error when I try to call the service from within the onReceive method after a broadcast event that I want to react on (time change).
How can I update the widget from onReceive after receiving a broadcast event?
Here is my widget
public class HijriWidget extends AppWidgetProvider{
...
@Override
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_TIME_CHANGED.equals(intent.getAction())||Intent.ACTION_DATE_CHANGED.equals(intent.getAction())){
//THE CODE BELOW LEADS TO AN EXCEPTION. HOW CAN I UPDATE THE WIDGET HERE?
RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.widget);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int appWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
appWidgetManager.updateAppWidget(appWidgetId, remoteView);
}
super.onReceive(context, intent);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
PendingIntent updatepending = HijriWidget.makeControlPendingIntent(context,RefreshService.UPDATE, appWidgetId);
pi = updatepending;
try {
updatepending.send();
} catch (CanceledException e) {
e.printStackTrace();
}
setAlarm(context, appWidgetId, 5*60*1000);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
public static PendingIntent makeControlPendingIntent(Context context, String command, int appWidgetId) {
Intent active = new Intent(context,RefreshService.class);
active.setAction(command);
active.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
//this Uri data is to make the PendingIntent unique, so it wont be updated by FLAG_UPDATE_CURRENT
//so if there are multiple widget instances they wont override each other
Uri data = Uri.withAppendedPath(Uri.parse("hijriwidget://widget/id/#"+command+appWidgetId), String.valueOf(appWidgetId));
active.setData(data);
return(PendingIntent.getService(context, 0, active, PendingIntent.FLAG_UPDATE_CURRENT));
}
public static void setAlarm(Context context, int appWidgetId, int updateRate) {
PendingIntent newPending = makeControlPendingIntent(context,RefreshService.UPDATE,appWidgetId);
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
if (updateRate >= 0) {
alarms.set( AlarmManager.RTC, calendar.getTimeInMillis(), newPending);
//alarms.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), updateRate, newPending);
} else {
// on a negative updateRate stop the refreshing
alarms.cancel(newPending);
}
}
}
and here is my service
public class RefreshService extends Service {
@Override
public void onStart(Intent intent, int startId) {
int appWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
RemoteViews remoteView = new RemoteViews(getApplicationContext() .getPackageName(), R.layout.widget);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
final Calendar c = Calendar.getInstance();
int mYear = c.get(Calendar.YEAR);
int mMonth = c.get(Calendar.MONTH);
int mDay = c.get(Calendar.DAY_OF_MONTH);
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
mMonth++;
HijriCalendar hc =new HijriCalendar( mYear,mMonth,mDay);
remoteView.setTextViewText(R.id.TextView02,hc.getHijriMonth()+"/"+hc.getHijriDay());
remoteView.setTextColor(R.id.TextView02, Color.BLACK);
remoteView.setTextViewText(R.id.TextView01, hc.getHijriYear()+"");
remoteView.setTextColor(R.id.TextView01, Color.WHITE);
appWidgetManager.updateAppWidget(appWidgetId, remoteView);
super.onStart(intent, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
Any help will be appreciated. Thanks.
UPDATE:
I've found the problem. Check my answer below.
I guess you've figured this out, but the problem in your original code was that the ACTION_TIME_CHANGED intent does not include a AppWidgetManager.EXTRA_APPWIDGET_ID extra, so getInt() was throwing a nullpointerexception.
Note: there is a slightly easier way to solve this problem: one of the overrides to updateAppWidget takes a componentname instead of a widget id, so you could create a common function to update the widget and call it from both onReceive and onUpdate, e.g.:
private static void updateWidget(Context context) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
// ... code here to build your remoteviews ...
ComponentName widgetComponent = new ComponentName(PACKAGE, PACKAGE+"."+CLASS);
appWidgetManager.updateAppWidget(widgetComponent, remoteView);
}
Then you never have to worry about widget ids. Of course this only works if you don't care about supporting multiple simultaneous widgets.
See: http://developer.android.com/reference/android/appwidget/AppWidgetManager.html#updateAppWidget%28android.content.ComponentName,%20android.widget.RemoteViews%29
UPDATE:
I've put the below code in the onReceive method and it worked for me
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisAppWidget = new ComponentName(PACKAGE, PACKAGE+"."+CLASS);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
try {
onUpdate(context, appWidgetManager, appWidgetIds);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With