Please start with the first part of this tutorial if you haven't read it before.
In this part we will build a "quote of the day" widget.
We will start with the layout. The widget is made of a Label for the text and an ImageView for the arrow button.
The layout in the designer:
You can see in the above picture that we use two panels. The base panel named pnlBase is a transparent panel (Alpha=0). The base panel contains another panel which is the grey panel.
The purpose of the transparent panel is to add some padding to the other views. The widget size is determined by the base panel. Without the transparent panel there will be no margin between the visible widget and the screen left edge.
We are setting the base panel size to 294x72. This is the recommended size for a 4x1 cells widget.
Tip: in some cases when you change the layout and there is already an existing widget in the device, the widget doesn't get updated. Remove the widget and add it again to see the change.
Now for the program logic.
Once a day the program fetches 20 quotes from 5 feeds available from Famous Quotes at BrainyQuote
Then the first quote is displayed. Each time the user presses on the arrow button the next quote is displayed.
While getting the quotes the first quote of each feed is added to the beginning of the quotes list. Only the first quote on each feed is new, and we want to start with the new quotes.
Downloading the feeds is done with HttpUtils and parsing them is done with XmlSax library. See the code for more information.
The widget is configured to be updated automatically every 24 hours. This is done in this line:
After 24 hours or when the widget is first added or after a boot the RequestUpdate event is raised.
First we clear the current quotes and then we fetch the new ones. Note that if the device was sleeping at this time then the calls are likely to fail as most devices turn off the wifi while sleeping. In this case new quotes will arrive when the user presses on the arrow button.
In cases like this you should not count on the automatic update to succeed and make sure that there is an alternative way to update the widget.
Persisting the data. The process running our widget code will not stay alive forever. It will be killed by the OS at some point.
Therefore we cannot rely on global variables to store our data.
All of the "state" variables must be written to a file.
RandomAccessFile.WriteObject and ReadObject are very useful for such tasks.
Each time that the widget sends a request to our application, Service_Start is called.
Not much is done in this sub:
This code allows RemoteViews to raise the correct event.
However if our process is not alive yet then Service_Create will be called before. Service_Create is an important point, as it allows us to read the previously saved state to memory:
The project is attached.
In this part we will build a "quote of the day" widget.
We will start with the layout. The widget is made of a Label for the text and an ImageView for the arrow button.
The layout in the designer:
You can see in the above picture that we use two panels. The base panel named pnlBase is a transparent panel (Alpha=0). The base panel contains another panel which is the grey panel.
The purpose of the transparent panel is to add some padding to the other views. The widget size is determined by the base panel. Without the transparent panel there will be no margin between the visible widget and the screen left edge.
We are setting the base panel size to 294x72. This is the recommended size for a 4x1 cells widget.
Tip: in some cases when you change the layout and there is already an existing widget in the device, the widget doesn't get updated. Remove the widget and add it again to see the change.
Now for the program logic.
Once a day the program fetches 20 quotes from 5 feeds available from Famous Quotes at BrainyQuote
Then the first quote is displayed. Each time the user presses on the arrow button the next quote is displayed.
While getting the quotes the first quote of each feed is added to the beginning of the quotes list. Only the first quote on each feed is new, and we want to start with the new quotes.
Downloading the feeds is done with HttpUtils and parsing them is done with XmlSax library. See the code for more information.
The widget is configured to be updated automatically every 24 hours. This is done in this line:
B4X:
'configure the widget and set it to update every 24 hours (1440 minutes).
rv = ConfigureHomeWidget("WidgetLayout", "rv", 1440, "Quote of the day")
B4X:
Sub rv_RequestUpdate
quotes.Clear
currentQuote = -1
HttpUtils.DownloadList("Quotes", Array As String("http://feeds.feedburner.com/brainyquote/QUOTEBR", _
"http://feeds.feedburner.com/brainyquote/QUOTEAR", "http://feeds.feedburner.com/brainyquote/QUOTEFU", _
"http://feeds.feedburner.com/brainyquote/QUOTELO", "http://feeds.feedburner.com/brainyquote/QUOTENA"))
End Sub
In cases like this you should not count on the automatic update to succeed and make sure that there is an alternative way to update the widget.
Persisting the data. The process running our widget code will not stay alive forever. It will be killed by the OS at some point.
Therefore we cannot rely on global variables to store our data.
All of the "state" variables must be written to a file.
RandomAccessFile.WriteObject and ReadObject are very useful for such tasks.
Each time that the widget sends a request to our application, Service_Start is called.
Not much is done in this sub:
B4X:
Sub Service_Start (StartingIntent As Intent)
If rv.HandleWidgetEvents(StartingIntent) Then Return
End Sub
However if our process is not alive yet then Service_Create will be called before. Service_Create is an important point, as it allows us to read the previously saved state to memory:
B4X:
Sub Service_Create
'configure the widget and set it to update every 24 hours (1440 minutes).
rv = ConfigureHomeWidget("WidgetLayout", "rv", 1440, "Quote of the day")
HttpUtils.CallbackActivity = "WidgetService"
HttpUtils.CallbackUrlDoneSub = "UrlDone"
HttpUtils.CallbackJobDoneSub = "JobDone"
parser.Initialize
'Load previous data if such is available.
'This is relevant in case our process was killed and now the user pressed on the widget.
If File.Exists(File.DirInternalCache, QUOTES_FILE) Then
raf.Initialize(File.DirInternalCache, QUOTES_FILE, True)
quotes = raf.ReadObject(0)
raf.Close
Else
quotes.Initialize
End If
If File.Exists(File.DirInternalCache, CURRENTQUOTE_FILE) Then
currentQuote = File.ReadString(File.DirInternalCache, CURRENTQUOTE_FILE)
End If
End Sub
The project is attached.
Attachments
Last edited: