With my external HDD trials and tribulations behind me, I have finally been able to complete a project I started last weekend – implementing an Android AppWidget. Along the way I have learned some more about both Android and Oxygene and what can be involved with working with the two together.
AppWidgets – A Quick Primer
AppWidgets on Android are those neat little applets which run on your device home screen (and/or lock screen). They often have a UI which is rendered directly on the home or lock screen, usually dynamic to some extent (not just an icon) and sometimes support some degree of interaction.
On Android, a widget is usually provided as part of an application though not always. It is perfectly possible to have a widget deployed on it’s own.
An Android AppWidget does have some similarity with a Windows 8 “live tile”.
There is (as yet at least) nothing even remotely similar on iOS at all.
What Are We Trying To Achieve Here ?
Before we get started, I should first explain the goal of my widget.
What I have in mind is a very simple widget that will display the current battery charge level in a plain, textual representation. Not very ambitious at all but actually something I would find immediately useful. And as we shall see, even this seemingly trivial project will introduce us to more of the Android OS than we might at first expect.
Getting Started with Oxygene
There are a number of things that are involved in adding a widget to an Android project:
- Provide a layout for the widget UI
- Implement an AppWidgetProvider to handle widget updates
- Provide meta-data describing the widget
- Identify the widget in the manifest
A lot of these are interconnected so there is no real logical order in which to go about it but the order in the list above avoids introducing references at any particular stage to things that do not yet exist, so it might be easiest to simply follow that order.
First create a new Android project. I call mine nz.co.deltics.batterywidget.
Using the Android Application template in Oxygene creates a project with a lot of things we don’t initially need:
- Remove (and delete) MainActivity.pas
- Remove (and delete) mainlayout.android-xml
- Edit values\strings.android-xml and remove all entries except app_name. Set this with a value of “Battery Widget“
- Edit AndroidManifest.android-xml and remove the <activity> entirely
AppWidgets have been a part of Android for a long time and so the minSdkVersion of 4 (Android 1.6 – Donut) is fine.
The AppWidgetProvider Class
The mechanism by which a widget updates its UI is somewhat more complicated than in an application activity. Since widgets are – or can be – visible on your device pretty much the whole time (unlike an application which is generally only visible while actually being used) having them constantly refreshing would be a potentially massive drain on the battery.
Instead, the visual representation of a widget is actually a static “snapshot”, capturing the state of the widget at a point in time. The widget state is updated on a periodic basis and the entity responsible for updating a widget is the AppWidgetProvider.
The end result is that we only have to declare a class which derives from AppWidgetProvider and overrides a single onUpdate method:
interface uses android.appwidget, android.content; type BatteryWidgetProvider = public class(AppWidgetProvider) public method onUpdate(aContext: Context; aWidgetManager: AppWidgetManager; aIDs: array of Integer); override; end;
We shall come back to the business of implementing this onUpdate method later. For now, Class Completion (yes Oxygene has that too:
[Ctrl] + [Shift] + C) will create an empty implementation for us.
There are other methods we could override to respond to our widget being added to – or removed from – the home screen, but I am not concerned about those in this case so we do not need to override them.
Next, we shall define the layout of our UI which this method will – somehow – update.
Just as with the UI of an Activity in an application, the layout of an AppWidget can be provided with an XML layout resource.
However, due to the different way in which widget UI’s are managed (specifically, involving the use of RemoteViews) there are some constraints on what can be used in a layout intended for use by a widget.
Fortunately, none of these constraints presents a problem for my simple UI, involving a simple RelativeLayout and a single TextView which will horizontally and vertically center a single item of text, displayed within the widget UI.
So, create a new XML file in the layout folder of our project. I called mine widgetlayout.android-xml.
<?xml version="1.0" encoding="utf-8" ?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="center"> <TextView android:id="@+id/lblInfo" android:text="..." android:gravity="center" android:layout_gravity="center" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
That’s it. The @+id/lblInfo attribute on the TextView assigns the lblInfo id to this element which we will be able to use in our code later to reference this component when we come to update our UI.
The text attribute provides the text that will be initially displayed by our widget. This will be visible briefly until our widget receives it’s first update notification, which is something to bear in mind when designing your initial layout.
AppWidgetProvider Info – Describing Our Widget
So far we have provided a class which will eventually update our widget and a layout which describes the UI but so far Android will still not know anything about our widget. Some of the things that the system needs to know includes how many widget “cells” our widget occupies, how often it would like to be updated, whether it can be resized or not.
All of this, and more, is provided in a meta-data resource which (like so much else on Android) takes the form of xml, in this case an appwidget-provider document.
We will add this in an xml resource folder in our project. First we add the xml folder to the existing res folder. xml is a sibling to the existing values, layout and various drawable… folders.
In this folder I then add a new xml file which I chose to call widgetinfo.android-xml.
NOTE: I should have mentioned that the android-xml extension on these files is necessary to trigger the code completion support for Android XML that is provided by Oxygene (since no actual Android schema exists, some mechanism had to be devised by which this knowledge – built into Oxygene itself – could be “awakened”).
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="72dip" android:minHeight="72dip" android:updatePeriodMillis="900000" android:initialLayout="@layout/widgetlayout"> </appwidget-provider>
This appwidget-provider declaration contains 3 key pieces of information about our widget:
- The minimum size and therefore space required by our widget
- The frequency of updates (15 minutes in this case)
- The initial layout for the widget UI
The minimum size specified equates to a 1 x 1 “cell” on a home screen.
The initial layout references the widgetlayout xml that we created previously. If a widget supports both home screen and lock screen use, a separate initial layout can be identified for the lock screen, if required. As implemented, this widget only explicitly supports home screen use so we do not need to worry about lock screen complications.
The updatePeriodMillis specifies a 15 minute update interval for the widget. The system does not guarantee to update on precisely this interval, and in this case I believe that Android may even disregard my requested frequency entirely, as being too frequent. I have seen 30 minutes stated as the minimum possible, but the Android SDK documentation implies that 15 minutes could be supported, which is why I chose to go with that 15 minute interval.
Suffice to say that there are alternative ways to obtain whatever frequency of update you require, but you should always be aware that your device will be woken from sleep to update your widget, so if you update too frequently or unnecessarily your widget will become a battery vampire.
In the case of my widget, I have in mind to implement more sophisticated update behaviours, but for the purposes of this exercise a 15 minute interval will do for now and if it results in an update of 30 minutes, well, that’s OK for now too.
But, even with this meta-data, Android still knows nothing about our widget because we have not as yet told it that any of this exists !
It’s All About Manifested Intents
The final, crucial step in bringing our widget to life is to add a declaration to our AndroidManifest file. As I explained fairly near the start, an AppWidget is simply a special case of a BroadcastReceiver so the existence of our widget takes the form of identifying the fact that we have such a receiver in our application:
<application ... > <receiver android:name="BatteryWidgetProvider" android:label="@string/app_name"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widgetinfo" /> </receiver> </application>
Our receiver identifies the name of the class that implements the response to the broadcast – the class that is our receiver. In the case of an AppWidget, this is the AppWidgetProvider class that we implemented: BatteryWidgetProvider.
The label is the descriptive name by which our widget will be identified in the list of available/installed widgets on our device and – as usual – we refer to the app_name string resource.
Receivers respond to intents. We saw, with the previous camera application series, that an activity could identify any intents that it responded to with an intent-filter, and so our receiver (appwidget) does the same. The one intent our widget currently responds to is the APPWIDGET_UPDATE – sent according to our requested update periodicity, as described in our widget meta-data.
And that meta-data is the final piece in the puzzle, identified by the meta-data tag of the receiver and pointing the system at the xml resource we created.
If we got everything right, we should now be able to build and run our project.
We can build all right, and our APK will be deployed on our device, but the debugger immediately halts since there is no application activity launched to which the debugger may attach:
I did a bit of investigating and found that in Eclipse there is a mechanism that seems similar to the notion of Delphi’s “Attach to process”, allowing you to hook up the Eclipse IDE to an existing, running process on an attached device (presumably AndroidStudio has some similar capability).
I wasn’t aware of any such capability in Visual Studio with Oxygene for Android however, and at first I thought I might be stuck.
But then I struck upon an idea.
My widget provider runs in the same process as any application that it is a part of. The Oxygene debugger seems to require an activity to attach to. Any activity in my application would be in the same process as the widget, so all I need to do is provide an activity and I should be OK.
The activity doesn’t need to do anything useful. It doesn’t even need a layout. I just need an empty activity.
So, an empty Activity class:
interface uses android.app; type DebugActivity = public class(Activity) end; implementation end.
And a declaration in the AndroidManifest, establishing this activity as our main launch activity:
<application ...> <activity android:label="@string/app_name" android:name="nz.co.deltics.batterywidget.DebugActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver ...> ... </receiver> </application>
And finally, to see if this little theory works, set a break-point on the “end” of the onUpdate method of the BatteryWidgetProvider class.
For some reason in Oxygene breakpoints cannot be set on the “begin” of a method, but for this little test that’s no big deal (and if there was code in the method we could set the breakpoint on the first line of that code – being able to break on begin is useful but also a bit weird when you think about it).
Now, when we run the application under the debugger, our application launches into the empty DebugActivity.
We can press the device “Home” button (hard or soft) to return to the home screen, but our application continues to run in the background. Now we can go to our widget gallery and see that our widget is listed:
Which we can now drag and drop to place on our home screen as we would any other widget.
Placing a widget on a screen triggers an initial update of the widget, and since our debugger is still attached to the running process, our breakpoint in the widget onUpdate method is triggered:
For an application which consists only of a widget it’s a bit annoying to have to remember to remove the activity declaration from the manifest prior to building a release and I’ve posted a question to RemObjects asking if there is a way to automate or at least simplify this (and if not, provided a suggestion of my own as to how it might be achieved quite neatly – imho).
But there it is.
As widgets go, this one isn’t very useful right now (not that it will be hugely useful even when finished) but it is a functioning widget.
Next time we will add the function itself – responding to the update notification by obtaining the current battery charge level and updating the widget UI.