Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Widget onReceive can't call Service class?

Tags:

android

widget

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.

like image 542
Majjoodi Avatar asked Jul 27 '10 18:07

Majjoodi


2 Answers

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

like image 195
Daren Robbins Avatar answered Nov 14 '22 04:11

Daren Robbins


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();

                }
like image 21
Majjoodi Avatar answered Nov 14 '22 05:11

Majjoodi