Thursday, October 31, 2013

A Mission to Android - Creating a Widget

So my mission continues and it's time to get my hands dirty and start coding the widget. I think everybody knows that a widget is a small program which can be placed on the home or the lock screen. A widget can show information or provide functions to interact with the system. To summarize a widget is just another means to interact with an app running in the background.

Overview

In today's post I will create the base class for my widget. During this post I hope to give you some inside on how a widget provider works and how it interacts with the android system. You will see how a view is prepared for display in a widget and on a side note you will get an overview into the log system of android.

Coding the AppWidgetProvider

Widget Lifecycle

  1. Widget is added for the first time to an AppWidgetHost like the home or lock screen. This is the time to allocate resources and start services needed for the operation of the widget.
  2. Widget needs to be updated or a new instance of the widget is added to an AppWidgetHost. This method is not called on the first addition of a widget instance. So make sure that any preparation for layouting is also done in the onEnabled() method.
  3. Widget is removed from an AppWidgetHost.
  4. Last instance of a widget is removed from an AppWidgetHost and the AppWidgetProvider is no longer needed. This is the right time to cleanup resources and stop any sticky services.
  5. Called when the widget is displayed or when it is resized. This is where you should calculate sizes and adjust views accordingly. Only available in API level 16 and higher.

All widgets are specialized broadcast receivers which execute stuff depending on special intents they receive from Android. Some of these intents are ACTION_APPWIDGET_UPDATE, ACTION_APPWIDGET_DELETED, ACTION_APPWIDGET_ENABLED, ACTION_APPWIDGET_DISABLED and more. You can get an overview taking a look at the AppWidgetManager documentation.

To ease creation and handling of widgets android provides a specialized class AppWidgetProvider which extends BroadcastReceiver and provides methods to hook into the life cycle of a widget. All you have to do is extend it and implement the methods.
If you take a look at the sources for AppWidgetProvider you will see that all life cycle methods are called from within the onReceive method by handling intents send from the OS.

So here is the code for my widget

/*
 * (c) Copyright 2013 Stefan Langer
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package de.element34.e34clock;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.util.Log;
import android.widget.RemoteViews;

public class E34ClockWidget extends AppWidgetProvider {
    private static final String TAG = "e34clock.E34ClockWidget";

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        Log.d(TAG, "E34ClockWidget created!");
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        Log.d(TAG, "E34ClockWidget completely removed!");
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
        Log.d(TAG,"Deleting an instance of widget!");
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        Log.d(TAG, "Updating widgets!");
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.clock_layout);
        appWidgetManager.updateAppWidget(appWidgetIds, views);
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }
}

The code is pretty straight forward and doesn't do anything seriouse yet. First thing to notice is that we extend AppWidgetProvider and as discussed earlier this gives us a couple of methods to interact with the lifecycle of the widgets. Important to note here is that this class will not handle a specific instance of your widget but all instances. The idea is that all widgets will present the same state of the application so there is no need for individual control of a single widget.

When the first instance of your widget is created the OS calls the onEnabled method giving you the chance to setup any resources you may need to interact with the backend process. For each new instance being created or whenever your widget needs to be updated do to events in the OS the onUpdate method is called. When an instance is deleted from the home- or lockscreen the onDeleted method is called. When the last instance is deleted and the OS no longer needs to interact with your widget the onDisabled method is called giving you a chance to cleanup and release all resources.

For now I simply log a message using the log system from android. This is exposed through the static object Log.

The log system supports 5 log levels. The list shows the log levels in descending order from most verbose to least verbose. When the log level is set all log levels that are lower in the list are also included, e.g. when the level is INFO the levels WARN and ERROR are also printed.

  • VERBOSE - exposed through the v method
  • DEBUG - exposed through the d method
  • INFO - exposed via the i method
  • WARN - exposed via the w method
  • ERROR - exposed via the e method
Each log method is prefixed with a tag. This tag can later be used to filter the log and see only messages relevant to your code. The tag is a simple string. It does not imply any hierarchy in the log system as does the jdk or log4j logging system. The string is simply prefixed to the log message to ease grepping of log output. A good convention is to save the prefix in a static final variable and reuse this throughout the class. I use TAG throughout my code as the name for this variable.

For the widget to be able to display something I have to setup the initial view. The place to do this is in the onUpdate method as this gets called whenever the screen needs to be updated. Be aware that the onUpdate method is not called when the widget is first enabled. If you need to do view customization which divert from your layout you have to also call your update code from onEnabled.

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        Log.d(TAG, "Updating widgets!");
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.clock_layout);
        appWidgetManager.updateAppWidget(appWidgetIds, views);
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

RemoteViews

A class that describes a view hierarchy that can be displayed in another process. The hierarchy is inflated from a layout resource file, and this class provides some basic operations for modifying the content of the inflated hierarchy.

Unlike in an Activity I do not have direct access to the View instead I have to create an instance of RemoteViews. This RemoteViews reads the layout xml and inflates it into a view hierarchy. I then use the AppWidgetManager to dispatch the views to all instances of the widget. You can steer which widgets are updated by specifying only the ids of the widget to update. Each id represents one instance of the widget. In my code I always treat all instances the same way and update every single one.

Resources

You can find further and more detailed information about developing widgets at App Widgets | Android Developers. For widget design take a look at Widgets | Android Developers and for design guide lines got to App Widget Design Guidelines

Outlook

In the next post I will post about the layout for the widget and I will talk about how android handles resources like images, strings and dimensions.

As always if you have any feedback please leave a comment. If you found errors or stuff I should do differently please tell me as this is also an endeavor for me to learn android.

Yours truely

Stefan Langer

Creative Commons License

No comments:

Post a Comment