How send data from application to AppWidgetProvider?

10,118

Solution 1

You have to create a service that updates the widget,check this tutorial (the 4th section), you may try to transfer the data through static variables.

Solution 2

I do this by adding extras in the intent that I build to update the widget. Also, when the widget has the information it can be passed again into the RemoteViewsService that populates the views for the widget

In my UpdateWidget helper method I pass in the userId (called by various activities in the app that may need to notify the widget that it needs to update itself).

public static void updateWorkoutsWidget(Context context) {
    Intent intent = new Intent(context, WorkoutsWidget.class);
    intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    intent.putExtra(WorkoutsWidgetService.USER_ID_EXTRA, userId);
    int[] ids = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, WorkoutsWidget.class));
    if(ids != null && ids.length > 0) {
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
        context.sendBroadcast(intent);
    }

Then the widget has a field to hold the userId and updates it in onReceive. I pass it into the RemoteViewsService with the Intent that is used to create the collection adapter.

public class WorkoutsWidget extends AppWidgetProvider {
    private long userId;

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getLongExtra(WorkoutsWidgetService.USER_ID_EXTRA, -1) != -1) {
             userId = intent.getLongExtra(WorkoutsWidgetService.USER_ID_EXTRA, -1);
        }
        super.onReceive(context, intent);
    }

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // update each of the app widgets with the remote adapter
        for (Integer widgetId : appWidgetIds) {
            Intent intent = new Intent(context, WorkoutsWidgetService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
            intent.putExtra(WorkoutsWidgetService.USER_ID_EXTRA, userId);
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));

            RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_workouts);
            rv.setRemoteAdapter(R.id.workouts_list, intent);

            rv.setEmptyView(R.id.workouts_list, R.id.empty_view);

            //(Pending intent stuff ommitted for brrevity)

            appWidgetManager.updateAppWidget(widgetId, rv);
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }
}

And then all that ends up passing straight on through the RemoteViewsService into its RemoteViewsFactory:

public class WorkoutsWidgetService extends RemoteViewsService {
    public static final String TRACK_WORKOUT_ACTION = "com.bodybuilding.mobile.service.TRACK_WORKOUT_ACTION";
    public static final String USER_ID_EXTRA = "userId";

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new WorkoutWidgetViewsFactory(this.getApplicationContext(), intent);
    }

    class WorkoutWidgetViewsFactory implements RemoteViewsFactory, ServiceConnection {

        WorkoutWidgetViewsFactory(Context context, Intent intent) {
            this.context = context;
            appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
            userId = intent.getLongExtra(USER_ID_EXTRA, -1);
        }

        //Lots of other stuff in here
    }
}
Share:
10,118
muneikh
Author by

muneikh

Experienced engineering leader specialized in growing and scaling tech organizations in building highly available/scalable software systems. Expertise: Android, iOS, Machine Learning

Updated on June 04, 2022

Comments

  • muneikh
    muneikh about 2 years

    I am stuck in a particular scenario. I need to get my widget updated as soon as the user update the time from the app. I did try Broadcast by sending the data through Intent Extras but fail to do so. Currently, I have my data in AppWidgetProvider and I need to send this data to service

    public class CountdownWidget extends AppWidgetProvider {
        // SharedPreferences userDefaults;
    
        // update rate in milliseconds
        public static final int UPDATE_RATE = 1800000; // 30 minute
    
        public static String nameOne = "";
    
        @Override
        public void onDeleted(Context context, int[] appWidgetIds) {
            for (int appWidgetId : appWidgetIds) {
                setAlarm(context, appWidgetId, -1);
            }
            super.onDeleted(context, appWidgetIds);
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
    
            Bundle extras = intent.getExtras();
            nameOne = extras.getString("NAME_ONE");
    
            Log.e("Name: ", nameOne);
    
            super.onReceive(context, intent);
        }
    
        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                             int[] appWidgetIds) {
    
            for (int appWidgetId : appWidgetIds) {
                setAlarm(context, appWidgetId, UPDATE_RATE);
            }
            super.onUpdate(context, appWidgetManager, appWidgetIds);
        }
    
        public static void setAlarm(Context context, int appWidgetId, int updateRate) {
    
            // Log.e("", "Updating Widget Service");
    
            PendingIntent newPending = makeControlPendingIntent(context,
                    CountdownService.UPDATE, appWidgetId);
            AlarmManager alarms = (AlarmManager) context
                    .getSystemService(Context.ALARM_SERVICE);
            if (updateRate >= 0) {
                alarms.setRepeating(AlarmManager.ELAPSED_REALTIME,
                        SystemClock.elapsedRealtime(), updateRate, newPending);
            } else {
                // on a negative updateRate stop the refreshing
                alarms.cancel(newPending);
            }
        }
    
        public static PendingIntent makeControlPendingIntent(Context context,
                                                             String command, int appWidgetId) {
            Intent active = new Intent(context, CountdownService.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("countdownwidget://widget/id/#" + command
                            + appWidgetId), String.valueOf(appWidgetId));
            active.setData(data);
            return (PendingIntent.getService(context, 0, active,
                    PendingIntent.FLAG_UPDATE_CURRENT));
        }
    }
    

    As you see nameOne is a static variable. I can receive data on the onReceive method using getExtras, but I am not unable to pass this data to my CountdownService Service.

    I did try CountdownWidget.nameOne but still fails to fet the data in the service.

    Any help will be highly appreciated.

  • muneikh
    muneikh almost 12 years
    Kindly see the updated question, I tried using the static variable but failed to get the data.