How to make an App Widget with Xamarin.Android

One of the things Android had for a long time were App Widgets. With these small apps, you’re able to create byte-sized functionality that users could use from their home and lockscreen. Since Android 5.0 the support for lockscreen app widgets was removed, while iOS10 introduced them again.

In this article we’ll dive into the way how you can create your own App Widget using Xamarin.Android. This article will cover a Hello World-example which you can take as a start to implement in your next project.

For this demo, I wanted to create a simple app widget which displays an icon, text and the current time. When you click the widget, the time will refresh but when you click the icon, an app will start. Let’s see how we can create such an app widget! The source code can be found on Github.

What we’ll be creating

Keeping the requirements stated above in mind, this is what we’ll be creating:

Goal

As you can see, it’s a pretty simple example but it just shows you the first steps on how to create an App Widget. Let’s see how you can do that yourself.

The layout

Assuming you already have your whole app solution up and running, let’s add a layout which will represent the app widget. You can add this on a later stage as well, I just find it easier to explain if we start with the layout. So to do this, simply create an AXML file in your /Layouts/ folder of your solution (in my example it’s called Widget.xaml). Here we’ll define the layout – I’ve stripped away any not important parts for now.


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/widgetBackground">
    <RelativeLayout android:layout_width="72dip">
        <ImageView
            android:id="@+id/widgetAnnouncementIcon"
            android:src="@drawable/announcement" />
    </RelativeLayout>
    <LinearLayout>
        <TextView
            android:id="@+id/widgetMedium"
            android:text="HelloAppWidget" />
        <TextView
            android:id="@+id/widgetSmall"
            android:text="Last refresh: 00:00" />
    </LinearLayout>
</LinearLayout>

It simply is an LinearLayout containing the background, which it it place contains the icon and two TextViews. Now that we have our layout, let’s continue in turning it into an widget.

The appwidget-provider-metadata

The first step into turning this into an actual app widget, is to add the appwidget-provider-metadata. As defined in the documentation: The AppWidgetProviderInfo defines the essential qualities of an App Widget, such as its minimum layout dimensions, its initial layout resource, how often to update the App Widget, and (optionally) a configuration Activity to launch at create-time.

To create this metadata, simply create an XML file with the appwidget-provider-element. In this example, I’ve added an appwidgetprovider.xml to the /Resources/XML/ folder. This file contains the following:


<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
	android:minWidth="288dip"
	android:minHeight="72dip"
	android:resizeMode="horizontal"
	android:minResizeWidth="144dip"
	android:updatePeriodMillis="1000"
	android:initialLayout="@layout/Widget"
	android:previewImage="@drawable/previewImage" />

This is a crucial part of turning it into an widget, so let’s briefly go over the most important attributes:

  1. initialLayout : The layout location for the widget that we defined in our previous step.
  2. previewImage : This is the image that you’ll see when selecting the widget in the widget overview (in my example, the Xamarin logo).
  3. updatePeriodMillis : This is how often Android should update the widget. Sadly you can’t set this to a value that’s too low, otherwise the widget will drain too much battery. This has a minimum value of 30 minutes. I just set my value lower, since the user can overrule this value manually by clicking on the widget and forcing it to refresh.

Now that we have our metadata defined, let’s dive into the next step to add more functionality to make it work.

The AppWidgetProvider-class

When developing for (native) Android, we would have to add stuff to our Manifest to tell Android there is an App Widget available. Luckily, since we’re using Xamarin, this can be done for our using the correct attributes above our classes. Simply create a class in your solution (in my example: AppWidget.cs) which has the following content:


[BroadcastReceiver(Label = "HellApp Widget")]
[IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE" })]
[MetaData("android.appwidget.provider", Resource = "@xml/appwidgetprovider")]
public class AppWidget : AppWidgetProvider
{
    public override void OnUpdate(Context context,
        AppWidgetManager appWidgetManager, int[] appWidgetIds) { }

    public override void OnReceive(Context context, Intent intent) { }
}

The Attributes on top of the file are needed to make the nescessary changes to the Manifest. You’ll also see the inheritence with AppWidgetProvider, which allows us to override the OnUpdate and OnReceive methods for the widget. Let’s start by filling in the OnUpdate-method and retrieve our layout:


public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
    var me = new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget)).Name);
    appWidgetManager.UpdateAppWidget(me, BuildRemoteViews(context, appWidgetIds));
}

private RemoteViews BuildRemoteViews(Context context, int[] appWidgetIds)
{
    var widgetView = new RemoteViews(context.PackageName, Resource.Layout.Widget);

    SetTextViewText(widgetView);
    RegisterClicks(context, appWidgetIds, widgetView);

    return widgetView;
}

Over here, you see the App Widget Manager retrieve the correct widget to work with and build the remove view. Let’s see how the private SetTextViewText and RegisterClicks methods work.

Set text

Since we’re working with RemoteViews, we sadly can’t use FindViewById. We’ll need to access the layout through this RemoteViews in order to update it:


private void SetTextViewText(RemoteViews widgetView)
{
    widgetView.SetTextViewText(Resource.Id.widgetMedium, "HelloAppWidget");
    widgetView.SetTextViewText(Resource.Id.widgetSmall,
        string.Format("Last update: {0:H:mm:ss}", DateTime.Now));
}

As you can see, the first text is just static. The second one is more dynamic and is being updated to the last updated time of the widget (manually by click or automatically by the updatePeriodMillis. At this point, we already have a very basic version of a app widget that we can use and change text on! But let’s take it one step further and add some click-events.

RegisterClicks

Now we’ll need to register the manual clicks on the widget. We’ll need to use an Intent to get that to work with a specific action. We’ll need to bind this to the background from the layout.


private void RegisterClicks(Context context, int[] appWidgetIds, RemoteViews widgetView)
{
    var intent = new Intent(context, typeof(AppWidget));
    intent.SetAction(AppWidgetManager.ActionAppwidgetUpdate);
    intent.PutExtra(AppWidgetManager.ExtraAppwidgetIds, appWidgetIds);

    // Register click event for the Background
    var piBackground = PendingIntent.GetBroadcast(context, 0, intent, PendingIntentFlags.UpdateCurrent);
    widgetView.SetOnClickPendingIntent(Resource.Id.widgetBackground, piBackground);
}

Now each time the background gets clicked, the OnUpdate-method gets called again which results in an update of the Layout. To take this one step further is being able to click on a button in the widget and act on that, for example: open up another app. Let’s see how we can achieve that!


private static string AnnouncementClick = "AnnouncementClickTag""";

private void RegisterClicks(Context context, int[] appWidgetIds, RemoteViews widgetView)
{
    widgetView.SetOnClickPendingIntent(Resource.Id.widgetAnnouncementIcon,
        GetPendingSelfIntent(context, AnnouncementClick));
}

private PendingIntent GetPendingSelfIntent(Context context, string action)
{
    var intent = new Intent(context, typeof(AppWidget));
    intent.SetAction(action);
    return PendingIntent.GetBroadcast(context, 0, intent, 0);
}

public override void OnReceive(Context context, Intent intent)
{
    base.OnReceive(context, intent);

    // Check if the click is from the "Announcement" button
    if (AnnouncementClick.Equals(intent.Action))
    {
        // Open another app
    }
}

As you can see, this step is a little bit more complex. We’ll need to define a static Tag and add that to the Indent. On the OnReceive-method from the Widget we’ll need to check if this was the actual Indent we just clicked.

Conclusion

That’s all there is to it, we now have met our requirements to create a very simple Android App Widget! From here on out you can take more steps to configure the widget to your needs. I hope I showed you the simplest way to create a very basic app widget. If you’re going to try it yourself, please take note of the Android App Widget design pattern as well. Don’t forget to poke around in the example code on Github and tell me what you think through Twitter!

Leave a reply:

Your email address will not be published.

Site Footer