Android Tutorial Android home screen widgets tutorial - part II

Status
Not open for further replies.

Erel

Administrator
Staff member
Licensed User
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:
B4X:
    'configure the widget and set it to update every 24 hours (1440 minutes).
    rv = ConfigureHomeWidget("WidgetLayout", "rv", 1440, "Quote of the day")
After 24 hours or when the widget is first added or after a boot the RequestUpdate event is raised.
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
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:
B4X:
Sub Service_Start (StartingIntent As Intent)
    If rv.HandleWidgetEvents(StartingIntent) Then Return
End 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:
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:

Jim Brown

Active Member
Licensed User
Nice work. Lots to learn from too

How do you go about finding details from sites which offer feeds in which data can be pulled from?
For example, in the quotes widget you have a few http : //feeds.feedburner.com/brainyquote/QUOTExx links
I tried looking around the brainyquote and feedburner sites but could not see any information relating to these. There must be a huge wealth of educational and fun resources out for feeding into daily widgets. I am not too clued up though on where to start though
 
Last edited:

Erel

Administrator
Staff member
Licensed User
There is no magic here. I browsed their site and found links to these feeds.
 

Qal3awy

New Member
Licensed User
widgets needs alot of concentration in oreder to understand how to do :)


can we get the same example of the widget , but i we want the quotes to be imported from the program itself , whether it was text file or writing quotes directly in the program ...

what i want is to make the program not depending on the internet , and the other thing is i want to write my own quotes ...

can we have example for that?
 

Inman

Well-Known Member
Licensed User
I have noticed that on the widgets of the official Engadget app and also the News and Weather widget that comes preloaded on phone, the currently displayed story changes every 5 seconds or so. That is, it basically caches a list of stories and changes them automatically without the user needing to press forward or backward arrow buttons to see the remaining stories. How is this done?
 

Erel

Administrator
Staff member
Licensed User
You can use a timer to update the widget every 5 seconds. However eventually the process will be killed by the OS. If you really want to keep it updating every 5 seconds all the time then you can use StartServiceAt to schedule the service to start every 5 seconds.
 

Inman

Well-Known Member
Licensed User
Ok. I already have a service for this widget that is scheduled to run every hour using StartServiceAt. This service pulls the list of data that is to be displayed on the widget (like your Quotes example). Now I need to cycle this data one by one on the widget, automatically, every 5 seconds. So does that mean I will have to run another service to do that?
 

Erel

Administrator
Staff member
Licensed User
You can use the same service. You might want to use a timer for the short intervals. You can reschedule the service with StartServiceAt in Service_Destroy.
 

wheretheidivides

Active Member
Licensed User
So I got that part. Let's say I just want to display a variable saved on SD card into the widget. Let's say this variable was saved by the program and records number of times you won playing the game.

I guess you need the panel.

Inside the panel would be 2 lables.
Label 1 would say 'wins: "
label 2 would be the vaiable.

So how do you put the variable into label2?
 

lxracer

Member
Licensed User
Quick Question

Hello Erel, great work on everything,
my question, is there a possibility to add 4 different sizes to my widget
say i make 4 different layouts or how would i go about it so user can
select which size of widget they want to use, say 1x1 1x2 1x3 1x4 and so on

Ive seen corwin42 multiple widget, but i want to do something a little different
which is add multiple widgets with different function,

im sure i can figure out most except how to add multiple sizes

thanks in advance :)
 

lxracer

Member
Licensed User
k

Thanks I thought maybe that and would 4 panels be fine or 4 different layouts I guess I just need to study your example more
 

Erel

Administrator
Staff member
Licensed User
It should work. Do you have internet connection? Check the logs for any error message.
 

sorex

Expert
Licensed User
Erel,

Can you please put the tip...

"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."

in bold, red whatever? (in both threads)

Wasted some time trying to figure out why my widget only could be placed at 3 locations on the entire screen as if it had a 200px margin added to the right and bottom.

If it was marked bold or something I would've found the solution faster ;)
and people who will try out widgets in the future might spot it faster and keep notice of it while testing.

Thanks!
 
Status
Not open for further replies.
Top