Android Tutorial Android home screen widgets tutorial - part I

Status
Not open for further replies.
Edit: widgets are handled with receivers now. See the attached example.

This tutorial will explain how to implement your own home screen widgets (also named App Widgets).

It is important to understand that the widgets are created and managed in another process, different than the process that your application is running in. The home screen application is hosting your widgets.
This means that it is not possible to directly access the widgets views. Instead we are using a special object named RemoteViews which gives us indirect access to the widget views.

Widgets do not support all views types. The following views are supported:
- Button (default drawable)
- Label (ColorDrawable or GradientDrawable)
- Panel (ColorDrawable or GradientDrawable)
- ImageView
- ProgressBar (both modes)

All views support the Click event and no other event.

The widget layout and configuration must be defined with XML files. During compilation Basic4android reads the layout file created with the designer and generates the required XML files.

Each widget is tied to a Service module. Through this module the widget is created and updated.

Creating a widget - step by step guide

- Add a Service module. Note that the service module handling the widget is a standard service.
- Design the widget layout with the designer. First add a Panel and then add the other views to this Panel.
The widget layout will be made from this panel.
- Add code similar to the following code the service module:
B4X:
Sub Process_Globals
    Dim rv As RemoteViews
End Sub

Sub Service_Create
    rv = ConfigureHomeWidget("LayoutFile", "rv", 0, "Widget Name")
End Sub

Sub Service_Start (StartingIntent As Intent)
     RV.HandleWidgetEvents(StartingIntent)
    Sleep(0)
    Service.StopAutomaticForeground
End Sub

Sub rv_RequestUpdate
    rv.UpdateWidget
End Sub

Sub rv_Disabled
    StopService("")
End Sub

Sub Service_Destroy

End Sub
- Compile and run your application. Go to the home screen, long press on the screen and you will see your widget listed on the widgets list.

ConfigureHomeWidget is a special keyword. At runtime it creates the RemoteViews object from the layout and set the events. At compile time the compiler generates the required files based on the arguments of this keyword.
The four parameters are: layout file, event name, update interval and the widget name.
Event name sets the subs that will handle the RequestUpdate and Disabled events.
The widget can be configured to update itself automatically. The interval, measured in minutes, defines how often will the widget request to update itself. Set to 0 to disable automatic updates. Updating the widget too often will have a bad impact on the battery. The minimum value is 30 minutes.
Widget name - the name that will appear in the widgets list.

As these arguments are read by the compiler, only strings or numbers are accepted.

Events:
B4X:
Sub Service_Start (StartingIntent As Intent)
    If rv.HandleWidgetEvents(StartingIntent) Then Return
End Sub
The above code checks the Intent message that caused this service to start and is responsible for raising the events related to the widget. It returns true if an event was raised.
The widget raises two events. RequestUpdate is raised when the widget needs to update itself. It will fire after adding the widget to the screen, after the device has booted, based on the scheduled updating interval (if set) or after the application was updated.
The Disabled event is raised when the last instance of our widget is removed from the screen.

As mentioned above all views support the Click event. All that needs to be done in order to handle the click event of a button named Button1 is to add a sub named Button1_Click (the sub name should actually match the EventName property which is by default the same as the name).

[/code]Modifying the widget:
It is not possible to directly access the widget views. Instead we need to use one of the RemoteView.Set methods.
If we want to change the text of a label named Label1 then we need to write the following code:
B4X:
rv.SetText("Label1", "This is the new text.")
'do more changes if needed
rv.UpdateWidget
After writing all the changes we call rv.UpdateWidget to send the updates to the widget.

A simple example is attached.
The example adds a simple widget. The widget doesn't do anything useful.
SS-2011.07.11-12.55.04.png

Note that it is recommended to test widgets in Release mode (the widget will fail in debug mode when the process is started by the OS).
 

Attachments

  • HomeWidgets.zip
    24.6 KB · Views: 823
Last edited:

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
And of course, in Service_Create you have used the ConfigureHomeWidget(layout, event, refreshtime, widget's name,false). If yes, sorry, I won't be able to help more. Only Erel and experts could
yes
 

Massimiliano

Member
Licensed User
Longtime User
Hello,
I have a widget with a timer showing a clock and a PhoneStateListener showing some cell data on events raise (onSignalStrengthsChanged and onCellLocationChanged).
After a while (1 hour about of phone sleeping), it seems that the timer event and/or PhoneStateListener events are not raised anymore (the widget labels doesn't refresh)

Any hint?
I'm using b4a v5.02

Thank you
 

lemonisdead

Well-Known Member
Licensed User
Longtime User
After a while (1 hour about of phone sleeping), it seems that the timer event and/or PhoneStateListener events are not raised anymore
please do You manage the PhoneWakeState ?
 

Massimiliano

Member
Licensed User
Longtime User
@lemonisdead
No, i'm not using phonewakestate, do you think this is the trick?

I thought that the power of the screen the sleeping services would return active. Isn't it?

Probaly I'm loosing something

I'll give it a try
Thank you
 

Erel

B4X founder
Staff member
Licensed User
Longtime User

Massimiliano

Member
Licensed User
Longtime User
I'll try Erel... Thank you ;-)
Erel it seems that adding
#StartCommandReturnValue: android.app.Service.START_STICKY
after a while it's raised an error at line

Sub Service_Start(StartingIntent As Intent)
--->>>> If rv.HandleWidgetEvents(StartingIntent) Then Return
End Sub

Any hint?
Thank you
b4a v5.02
 

Massimiliano

Member
Licensed User
Longtime User
There is an error at wgtservice.java un line 201, object should be initialized first

B4X:
public static String  _service_start(anywheresoftware.b4a.objects.IntentWrapper _startingintent) throws Exception{
//BA.debugLineNum = 22;BA.debugLine="Sub Service_Start(StartingIntent As Intent)";
//BA.debugLineNum = 24;BA.debugLine="PSL_SVC.startListening";
_psl_svc.startListening();
//BA.debugLineNum = 26;BA.debugLine="If rv.HandleWidgetEvents(StartingIntent) Then Ret";
if (_rv.HandleWidgetEvents(processBA,(android.content.Intent)(_startingintent.getObject()))) { //<---- THIS IS LINE 201
if (true) return "";};
//BA.debugLineNum = 27;BA.debugLine="End Sub";
return "";
}

removing #StartCommandReturnValue: android.app.Service.START_STICKY the problem doesn't appear anymore
 

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
Hello,
how can I set the image of the buttons inserted?
 

Massimiliano

Member
Licensed User
Longtime User
I see. Change the code to:
B4X:
If StartingIntent.IsInitialized AND rv.HandleWidgetEvents(...)

In sticky mode an empty intent is sent when the process is recreated.

Hi Erel this was perfect....

But I have another question :)

My app together with the widget sticky service has other activities. You can launch these activities tapping the widget logo or launching the application in the standard way.
When android starts the widget takes only 3 mb RAM.
if i start an activity and then i close it, memory consumption grows and never decreases when all activities are closed (i'm pretty sure
Activity.Finish is called)

any suggestions?

Thanks
Massimiliano
 

Massimiliano

Member
Licensed User
Longtime User
Are you using B4A v5? There was a memory leak in v4 related to layout animations.

Yes, i'm using v5.02
My android is Lollipop 5.1 (released by Motorola 3 days ago in italy via OTA) on my moto-g-lte-xt1039, but also with Kitkat 4.4.4 i've found this problem, so i'm pretty sure it's not related to an android problem.
The memory consumption (widget only) is 3.2mb after a phone restart.
After launching some activities inside the apk package the memory consumption grows (sometime it goes over 100mb).
for sure i'm using a lot of libraries, but i expect that on Activity.finish all should be released from memory

I tried ExitApplication, but it's like i was Killing myself with my witget service! :)
 
Last edited:

corwin42

Expert
Licensed User
Longtime User
After launching some activities inside the apk package the memory consumption grows (sometime it goes over 100mb).
for sure i'm using a lot of libraries, but i expect that on Activity.finish all should be released from memory

If you call Activity.finish all resources bound to this activity are marked as unused and in the next GC cycle they will be freed. The application process itself will keep running and if you store data in process global variables this memory will still be used. If you use many bitmaps they probably may stay in memory, too for some time.
So without knowing your application and code we can't say if it is ok or not. But this whole memory stuff is not related to this tutorial so if you have further questions please start a new thread on this in the questions forum.
 

Massimiliano

Member
Licensed User
Longtime User
The memory usage is allowed to grow. The question is whether you eventually get an out of memory exception?
No memory exception, but lot of mem consumption compared to other apps.

I know this thread isn't the right place. I will open the right one, if I will be able to give more information.

Many thanks
 

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
Hello,
I tried to implent into the event "ImageView_click" this code

B4X:
Sub iv1_Click
Dim i as ImageView
i = Sender
End Sub

But the app crash.
Here it is the log:

B4X:
java.lang.ClassCastException: anywheresoftware.b4a.objects.RemoteViewsWrapper cannot be cast to android.widget.ImageView

I can't do it?
 

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
You cannot directly access the widget views.
Sender will be RemoteView not ImageView.

So, how can I get the name of all imageviews on the widget?
 
Status
Not open for further replies.
Top