Android Tutorial Update a homescreen widget more frequently than 30 minutes

Normally a homescreen widget can only update itself every 30 minutes. A lower value is not possible in the ConfigureHomeWidget() call.

But what if you want to create something like a clock which needs to be updated every minute?

This is possible too but I experienced some problems handling the widgetservice correctly in this case. If it is not handled correctly the service will run forever even if no widget is visible on the homescreen anymore. I will explain it in this tutorial on a simple clock widget example.

B4X:
Sub Service_Create
   rv = ConfigureHomeWidget("l2x1", "rv", 120, "AmberHome Minimalistic Clock")
End Sub

We configure our widget in Service_Create(). The update interval can be set to 0 here. It really shouldn't matter because we update the widget ourself. I used a value of 120 because I wanted to be sure that the clock will restart at least after 2 hours when an unexpected problem occurs.

B4X:
Sub Service_Start (StartingIntent As Intent)
   ' If a widget is removed from homescreen we don't need to do anything
   If StartingIntent.Action = "android.appwidget.action.APPWIDGET_DELETED" Then Return
   
   ' Handle the widget events
   If rv.HandleWidgetEvents(StartingIntent) = False Then
      ' If the action is not handled by HandleWidgetEvents() then we
      ' probably were called by StartService() or StartServiceAt().
      ' So just update the widget.
      rv_RequestUpdate
   End If
   
   ' We have to be sure that we do not start the service
   ' again if all widgets are removed from homescreen
   If StartingIntent.Action <> "android.appwidget.action.APPWIDGET_DISABLED" Then
      StartServiceAt("", TimeToNextMinute, False)
   End If
End Sub

To handle the service correctly we have to react correctly on the Intent actions. The commets should explain everything here enough.

If a widget (not the last one) is removed from the homescreen nothing has to be done so we just return.

If the Events are handled by HandleWidgetEvents() then we don't need to do anything special. Otherwise we just update the widget.

At last we have to reschedule our service at the next full minute. Attention: We have to be sure that we don't reschedule our service when the last widget is removed from homescreen. Otherwise our service won't stop correctly!

B4X:
Sub rv_Disabled
   CancelScheduledService("")
   StopService("")
End Sub

If the last widget is removed we have to cancel the last schedule and completely stop the service.

The other subs in the program are trivial. Look at the example code.

PS: I'm always amazed how simple and fast it is to create apps with B4A!
 

Attachments

  • AHClock.zip
    5.7 KB · Views: 1,363
Last edited:

susu

Well-Known Member
Licensed User
Great thread! Thank you so much.
 

nico78

Active Member
Licensed User
Hello,

Can you help me, please.

how would you do to update the widget to 10:00 every day for example?
 

corwin42

Expert
Licensed User
Hello,

Can you help me, please.

how would you do to update the widget to 10:00 every day for example?

You just need to calculate the timestamp for 10:00 the next day and use this in StartServiceAt().
 

nico78

Active Member
Licensed User
I understand, every day at 10 o'clock gives:
B4X:
   ret=DateTime.TimeParse("10:00")
   ret = ret + DateTime.TicksPerDay


@corwin42,
I do not understand this linen in your code:
B4X:
ret = ret - (ret Mod DateTime.TicksPerMinute)

can you explain the purpose!
 

corwin42

Expert
Licensed User
Its completely this in the code.
B4X:
ret = DateTime.Now + DateTime.TicksPerMinute
ret = ret - (ret Mod DateTime.TicksPerMinute)

The first line just adds one minute to the current time

The result of (ret Mod DateTime.TicksPerMinute) is the number of milliseconds we are over the full minute and this gets substracted from current time + one minute. So the Sub just returns the ticks value of the next full minute.
 

nico78

Active Member
Licensed User
ok, it is ultimately the equivalent of this:
B4X:
DateTime.TimeFormat = "HH:mm:ss"
ret = (DateTime.Now + DateTime.TicksPerMinute)
ret=DateTime.TimeParse(DateTime.GetHour(ret)&":"&DateTime.GetMinute(ret)&":00")
 

corwin42

Expert
Licensed User
ok, it is ultimately the equivalent of this:
B4X:
DateTime.TimeFormat = "HH:mm:ss"
ret = (DateTime.Now + DateTime.TicksPerMinute)
ret=DateTime.TimeParse(DateTime.GetHour(ret)&":"&DateTime.GetMinute(ret)&":00")

Yes, nearly. But there is a problem at midnight with your solution. At 23:59:xx your solution will return 00:00:00 of the current day, not the next day. So perhaps you won't notice it but the service will be started permanently one minute long and maybe produce high cpu load for a minute.
 

splatt

Active Member
Licensed User
Hi Marcus,

What triggers the calls to HandleWidgetEvents?

Why does this bypass the UpdateMinutesInterval value set when you configure the widget?
 

corwin42

Expert
Licensed User
Hi Marcus,

What triggers the calls to HandleWidgetEvents?

Why does this bypass the UpdateMinutesInterval value set when you configure the widget?

The service starts itself with StartServiceAt() every minute.

With this, could you make a widget with a framerate bigger than 1?

yes, of course
 

squaremation

Active Member
Licensed User
This example is not working for me? only shows 12:34 as time. Is there something I need to do to get it to work with b4a 3.2 version?
 

corwin42

Expert
Licensed User
This example is not working for me? only shows 12:34 as time. Is there something I need to do to get it to work with b4a 3.2 version?

Works as expected here with B4A 3.80 and should work with B4A 3.20, too.
Do you see something in the (unfiltered) logs?
 

squaremation

Active Member
Licensed User
Works as expected here with B4A 3.80 and should work with B4A 3.20, too.
Do you see something in the (unfiltered) logs?

Just picked up b4a again and forgot I needed to put in 'release' mode.:oops:
 

Mark Zraik

Member
Licensed User
Normally a homescreen widget can only update itself every 30 minutes. A lower value is not possible in the ConfigureHomeWidget() call.

But what if you want to create something like a clock which needs to be updated every minute?

This is possible too but I experienced some problems handling the widgetservice correctly in this case. If it is not handled correctly the service will run forever even if no widget is visible on the homescreen anymore. I will explain it in this tutorial on a simple clock widget example.

B4X:
Sub Service_Create
   rv = ConfigureHomeWidget("l2x1", "rv", 120, "AmberHome Minimalistic Clock")
End Sub

We configure our widget in Service_Create(). The update interval can be set to 0 here. It really shouldn't matter because we update the widget ourself. I used a value of 120 because I wanted to be sure that the clock will restart at least after 2 hours when an unexpected problem occurs.

B4X:
Sub Service_Start (StartingIntent As Intent)
   ' If a widget is removed from homescreen we don't need to do anything
   If StartingIntent.Action = "android.appwidget.action.APPWIDGET_DELETED" Then Return
 
   ' Handle the widget events
   If rv.HandleWidgetEvents(StartingIntent) = False Then
      ' If the action is not handled by HandleWidgetEvents() then we
      ' probably were called by StartService() or StartServiceAt().
      ' So just update the widget.
      rv_RequestUpdate
   End If
 
   ' We have to be sure that we do not start the service
   ' again if all widgets are removed from homescreen
   If StartingIntent.Action <> "android.appwidget.action.APPWIDGET_DISABLED" Then
      StartServiceAt("", TimeToNextMinute, False)
   End If
End Sub

To handle the service correctly we have to react correctly on the Intent actions. The commets should explain everything here enough.

If a widget (not the last one) is removed from the homescreen nothing has to be done so we just return.

If the Events are handled by HandleWidgetEvents() then we don't need to do anything special. Otherwise we just update the widget.

At last we have to reschedule our service at the next full minute. Attention: We have to be sure that we don't reschedule our service when the last widget is removed from homescreen. Otherwise our service won't stop correctly!
Did you need to add any additional Manifest Text for the android.appwidget.action.APPWIDGET_DISABLED or
android.appwidget.action.APPWIDGET_DELETED intent-filter, or does this method of checking the StartIntent.Action you use handle that without the intent filters in the manifest file??
 

corwin42

Expert
Licensed User
Did you need to add any additional Manifest Text for the android.appwidget.action.APPWIDGET_DISABLED or
android.appwidget.action.APPWIDGET_DELETED intent-filter, or does this method of checking the StartIntent.Action you use handle that without the intent filters in the manifest file??

No, no special intent filter needed.
 

Mark Zraik

Member
Licensed User
Thank you Corwin,

That helps me understand a whole lot more.
 

erosmax

Well-Known Member
Licensed User
Hi, why my widget control automatically closes after a few hours? :eek::(
I've setting my ConfigureHomeWidget to 0 for update.
 

erosmax

Well-Known Member
Licensed User
Opps sorry. I was wrong. :oops:
However I have resolved
 
Top