Updating AppWidget Periodically From Service

This is what I want from my AppWidget:

  • configuration activity appears when the widget is added to the screen // still good
  • after saving the configuration, a service is launched that updates the widget // still
  • Schedule an alarm periodically to start a service that updates the widget. // There are problems here.

This already seriously gives me gray hair, and I don’t know what to do next. How to set update speed for AppWidget from a service? I can update the widget from the service, but when I try to set the alarm, it does not fall into the onReceive() method in the AppWidget.

Here is the service update code:

 Intent updateWidget = new Intent(); updateWidget.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); updateWidget.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]{appWidgetId}); Uri data = Uri.withAppendedPath(Uri.parse(WeatWidgetProvider.URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId)); updateWidget.setData(data); PendingIntent updatePend = PendingIntent.getBroadcast(this, 0, updateWidget, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE); alarm.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime()+ updateRate, updateRate, updatePend); 

And in onReceive() WidgetProviderClass:

 public void onReceive(Context context, Intent intent){ super.onReceive(context, intent); Log.d("OnReceive", "OnReceive called"); String action = intent.getAction(); Log.d("Action", "OnReceive:Action: " + action); if(AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)){ int appwidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); if(appwidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){ this.onDeleted(context, new int[] {appwidgetId}); } } else if(AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)){ if(!URI_SCHEME.equals(intent.getScheme())){ final int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); for(int appWidgetId:appWidgetIds){ SharedPreferences prefs = context.getSharedPreferences(WeatForecastConfigure.WEATHER_PREF_NAME, Context.MODE_PRIVATE); update = prefs.getInt(WeatForecastConfigure.REFRESH_UPDATE, -1); System.out.println(update); if(update != -1){ Intent widgetUpdate = new Intent(); widgetUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId }); // make this pending intent unique by adding a scheme to it widgetUpdate.setData(Uri.withAppendedPath(Uri.parse(WeatWidgetProvider.URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId))); PendingIntent newPending = PendingIntent.getBroadcast(context.getApplicationContext(), 0, widgetUpdate, PendingIntent.FLAG_UPDATE_CURRENT); // schedule the updating AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); alarms.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), WeatForecastConfigure.convertToMillis(update), newPending); } } } super.onReceive(context, intent); }else{ super.onReceive(context, intent); } } 

For onUpdate() commented lines are intentional.

 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){ Log.d("OnUpdate", "OnUpdate called"); for (int appWidgetId : appWidgetIds) { // context.startService(new Intent(context,WeatService.class)); } //super.onUpdate(context, appWidgetManager, appWidgetIds); } 

Please, help. I do not know how to set the alarm for updates. I am doing this from a configuration class or from a service, this does not work. Thanks.

+4
source share
2 answers

I think you need to use the standard URI scheme in your intentions as follows:

 intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); 
0
source

I wrote all of this, and realized that it would probably just confuse you more, oh well.

Here is a skeleton on how I update my widgets, it is freely based on this code http://code.google.com/p/android-sky/source/browse/#svn%2Ftrunk%2FSky (in particular, the UpdateService class.

The main difference is my code

  • Starting a service to perform an upgrade
  • Sets an alarm (pending intent) to alert the widget about the next update
  • Updates the widget and stops the service
  • Receives broadcast updates (on onReceive providers) and starts the service again

It seems you are trying to tell AppWidgetProvider to run through onUpdate, I'm not sure if this is possible?

Here's how I do it in semi pseudo code:

Class UpdateService:

  //based on (an old) example http://code.google.com/p/android-sky/source/browse/#svn%2Ftrunk%2FSky however other methods I have tried have not been as reliable as this public void run() { //set the alarm for the next update AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); long millis = System.currentTimeMillis(); //one alarm to update them all, if you wanted to you could //add an extra appWidgetId then you might need to encode the intent //however so it is a unique PendingIntent Intent updateIntent = new Intent(ACTION_UPDATE_ALL); //ACTION_UPDATE_ALL should be a constant somewhere that can be used to resolve this action com.packagename.ACTION_UPDATE_ALL PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, updateIntent, 0); // Schedule alarm, and force the device awake for this update alarmManager.set(AlarmManager.RTC, millis + updateFrequency * DateUtils.MINUTE_IN_MILLIS, pendingIntent); Log.d(TAG, "Next update should be at: " + DateFormat.format("h:mm:ss", millis + updateFrequency * DateUtils.MINUTE_IN_MILLIS)); while (hasMoreUpdates()) { int appWidgetId = getNextUpdate(); if (!(appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID)) { Uri appWidgetUri = ContentUris.withAppendedId(Agenda.getContentUri(context), appWidgetId); AppWidgetProviderInfo info = manager.getAppWidgetInfo(appWidgetId); String providerName = info.provider.getClassName(); //if the provider matches one of my widget classes, call the update method if (providerName.equals(Widget_4_1.class.getName())) { remoteViews = Widget_4_1.buildUpdate(context, appWidgetUri, dateRows); } //perform more actions such as sending remoteViews to the AppWidgetManager } } } 

Widget_4_1 Class:

 @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // If no specific widgets requested, collect list of all if (appWidgetIds == null) { appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName( context, AbstractWidget.class)); } UpdateService.requestUpdate(appWidgetIds); Intent i = new Intent(); i.setComponent(new ComponentName(context, UpdateService.class)); context.startService(i); } //called from service public static RemoteViews buildUpdate(Context context, Uri appWidgetUri) { int appWidgetId = (int) ContentUris.parseId(appWidgetUri); RemoveViews remoteViews = someMethodToBuildView(appWidgetId ); //return view to service return remoteViews; } public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); //when the alarm triggers, just update all your widgets, why handle them separately? if (action.equals(ACTION_UPDATE_ALL)) { AppWidgetManager manager = AppWidgetManager.getInstance(context); int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, getClass())); if(appWidgetIds.length>0){ UpdateService.requestUpdate(appWidgetIds); Intent updateIntent = new Intent(context, UpdateService.class); updateIntent.setAction(action); context.startService(updateIntent); } } //else you could catch a specific action for updating a single widget and //tell your update service to do it manually } 

AndroidManifest.xml:

 <application> ... <receiver android:name="com.mypackage.Widget_4_1" android:label="@string/agenda_widget_name_4_1"> <intent-filter> <!-- IMPORTANT, needs to match the ACTION_UPDATE_ALL string (eg a constant somewhere) --> <action android:name="com.packagename.UPDATE_ALL" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_4_1" /> </receiver> ... </application> 

I hope I didn’t confuse you.

Since this code is copied / pasted from different parts of my project, sorry typos, etc., I will fix them as they are indicated.

0
source

Source: https://habr.com/ru/post/1348130/


All Articles