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: 566
Last edited:

peggjones

Active Member
Licensed User
Longtime User
Basic4android v1.6 adds support for home screen widgets. 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)
    If rv.HandleWidgetEvents(StartingIntent) Then Return
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).
For example if you want to show the main Activity when the user presses on Button1 you can use this code:
B4X:
Sub Button1_Click
StartActivity(Main)
End Sub
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


Second part of this tutorial: http://www.b4x.com/forum/basic4andr...oid-home-screen-widgets-tutorial-part-ii.html
 

shaxboz

Member
Licensed User
Longtime User
Hi! I created one widget. It is normally working on android 2.3, but when I install to android 4, OS is not showing my widget in widget panel and I cannot install to desktop of android 4?
What should I do?
 

ITSB4A

New Member
Licensed User
Longtime User
Hi All,

I've read all the articles and threads related to Widget creation and in particular anything with the keywords "Click Event not firing" but have been really surprised not to find a solution. Surely others are having this problem too?

Basically using the Widget samples from Tutorial P1 and P2 the click events of any view are not firing. I've even stripped back the P2 example and re-created the layout with no views (i.e. Just using the Panel1 click) and also just adding a button view and the relevant click event but the events simply don't fire.

Until just before the creation of this thread, all testing has been done on 2 different devices but they are both running Android 4.4.2. The main device is a Samsung Galaxy S4, which I'm sure a lot of you must be testing with? Anyway, I though it would be best to try an older device and pulled out an HTC handset running Android 2.3.5,installed the same samples and my modified one and the click events fired straight away.

My question is therefore is there a compatibility issue with B4A widgets and Android 4.4.2 or is this a working combination for others who are developing or testing widgets that is fine?

Anyone's help in this matter would be greatly appreciated as I'm starting to lose more hair than someone of my age really should ;)
 

ITSB4A

New Member
Licensed User
Longtime User
Hi Erel,

Thanks for your reply.

Yes, as advised I'm testing in release mode although as a test I did try 'Debug Legacy' mode and added some logging. The 'Request Update' and service messages were all fired but never the view click events. I thought maybe the toast message were not supported so created a new activity which is called from the click event. It worked on the Android 2.3.5 device as well as the toast message but not on the Android 4.x devices.

I'm using the latest B4A 3v82 ahhh! Sorry, found the problem and can't remember changing the SDK Platform but it was set to a v19 rather than 8 as you suggest. I've changed this back to 8 and the widgets have started working.

Hopefully this may help someone else out perhaps?
 

ITSB4A

New Member
Licensed User
Longtime User
That's strange then as the only thing that changed was going back to v8 of the SDK platform.

Something else I've noticed is that if an exception error occurs executing the widget this can then prevent the widget from firing subsequent click events until the phone is rebooted. Using Android Task Manager to close all apps and removing and replacing the widget doesn't work. Everything appears to be okay and the Widget displays fine but the click events don't fire on pressing the image view or panel. As soon as the phone is rebooted the widget starts working fine again.

The errors have been sorted out now and try/catch statements added so all seems to be well now but it would be interesting to know your take on what might be happening after the crash and if there is a way of clearing the problem without having to reboot the device?
 

ITSB4A

New Member
Licensed User
Longtime User
Thanks Erel and will try and feedback any other information I find that maybe useful to others. Great to have it working now though!
 

Mrjoey

Active Member
Licensed User
Longtime User
- Button (default drawable)
- Panel (ColorDrawable or GradientDrawable)
Mr Erel , it s sad that the buttons and panels are limited not using statelistdrawble , since i really need to have statelist drawble buttons and panels with backgrounds , is that possible?
 

Wembly

Member
Licensed User
Longtime User
Hi

Please can you help I think I'm missing something here...

I've got a widget working fine and have added the following code to open the main app for the widget if its clicked

B4X:
Sub widget_Click

    Try
               
        Dim i As Intent

        i.Initialize(i.ACTION_MAIN, "")
                  
        i.SetComponent("uk.co.androidapps4u.ShiftCalendar/.main")


        StartActivity(i)

        Catch

'        Widget.SetVisible("PopupPanel", True)

            widget.UpdateWidget   

        End Try
               
End Sub

This works if my app is running in the background but if I clear the app from memory of the device and click the widget I get an error that the app has stopped.

I assume my app does not always have to be running for the widget to be able to launch it if clicked?

Any help much appreciated!

Thanks
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I assume my app does not always have to be running for the widget to be able to launch it if clicked?
You are correct. Make sure to run your app in Release mode. If it still crashes then you need to check the logs (connect with USB debug mode).

but im asking u if there is a way to use buttons with statelistdrawable instead of standard drawable?
You can manually create the XML layout file and then you will probably be able to use StateListDrawable.
 

Mrjoey

Active Member
Licensed User
Longtime User
You are correct. Make sure to run your app in Release mode. If it still crashes then you need to check the logs (connect with USB debug mode).


You can manually create the XML layout file and then you will probably be able to use StateListDrawable.
there is a xml file located in "Objects\res\layout" called "widgetservice_layout" , i tried to edit it , but each time i compile i noticed that it will create a new one and overwrite it by the compiler , and i tried to set the file to read-only , i got :
B4X:
res\layout\widgetservice_layout.xml:5: error: Error: No resource found that matches the given name (at 'background' with value '@drawable/widgetservice_pnlbase_background').
res\layout\widgetservice_layout.xml:9: error: Error: No resource found that matches the given name (at 'src' with value '@drawable/backwardsmall').
res\layout\widgetservice_layout.xml:10: error: Error: No resource found that matches the given name (at 'src' with value '@drawable/playsmall').
res\layout\widgetservice_layout.xml:11: error: Error: No resource found that matches the given name (at 'src' with value '@drawable/forwardsmall').
 
Status
Not open for further replies.
Top