Miskatonic University Press

Making the Days Old widget

android

I like to keep track of how many days old I am. It's a memento mori with the added benefit that every thousand days I get to celebrate an extra birthday and have a party. (If you're curious about how many days old you are, just put your birth date into Wolfram|Alpha and it will tell you.)

Last week I made an app widget for my Android phone to show me the number of days. It looks like this: it's the black box with the white numbers in it (with the exact number of days obscured, for security, not embarrassment):

Days Old widget

(On the left is another widget, and on the right is the Wikipedia app. In case you're curious, the icons on the top left show that I have voice mail, that there's a USB connection, that I'm in developer mode, that Tasker is running, and that I'm at home (I use Tasker to trigger some actions automatically when I'm at home). I took this screenshot with DDMS.)

I'll show you how I made the widget, but first, a step back. Let's pretend I was born on Unix epoch day, 1 January 1970. Asking Wolfram|Alpha tells us that was 15570 days ago (I'm writing this on 18 August 2012).

Finding the number of days between two dates is a common question asked by people using all programming languages, and a quick web search will turn up lots of examples. Here's one way to do it in Perl:

#!/usr/bin/env perl

use Date::Calc qw(Delta_Days);

my @date_now = (localtime)[5,4,3];
$date_now[0] += 1900;
$date_now[1]++;

my @date_then = (1970, 1, 1);

my $days = Delta_Days(@date_now, @date_then);
print "$days\n";

Ruby's Date class lets you subtract one day from the other (and then turn the result into an integer):

#!/usr/bin/env ruby

require 'date'

date_then = Date.parse("1970-01-01")
date_now = Date.today

puts (date_now - date_then).to_i

I used to use a Perl script to tell me how many days old I am, but that meant I had to remember to run it. I wanted to have the number in front of me where I could always see it. For that an app widget is just the thing: "App Widgets are miniature application views that can be embedded in other applications (such as the Home screen) and receive periodic updates."

I'd never seriously looked into how to write programs for Android, but it turned out to be fairly easy to cobble something very simple together based on some examples people had posted. (Cargo cult programming is always how I get started on something new like this.) Google's documentation is very well-written and useful.

I started at the Developer Tools site, which covers getting the Android software development kit and installing Android Development Tools, a plugin for Eclipse. I was really impressed with the Eclipse integration. The last time I used Eclipse was for some Ruby on Rails work, and that was nice, but I'd never seriously used it for programming the way some people (especially Java programmers, I guess) do. I had no idea how powerful it was or what a plugin like this could do.

Eclipse in action, working on this program

When I got started trying to write a program, Android Training explained all the basics very clearly and thoroughly. The Eclipse plug-in lets you say "make a new Android project" and it sets out all the files you need and starts you off with a simple program you can adapt. Running Your App shows how to run a virtual Android phone for testing, which is fun.

For actually making my app widget, I copied what I saw in these three examples:

Through all of that I was referring to the App Widgets documentation to figure out what something meant or what options I had or what I might add.

There are just four files that make the widget. I think that's the minimum for any widget.

First, AndroidManifest.xml, which is necessary in any Android application. The full docs on this file explain, "Every application must have an AndroidManifest.xml file (with precisely that name) in its root directory. The manifest presents essential information about the application to the Android system, information the system must have before it can run any of the application's code."

Mine looks like this. It says it's an application and there's a "receiver" (which is how widgets work) named org.miskatonic.daysold.DaysOldWidget (these things are always based on a domain name) and also a "provider" to be found in @xml/daysoldwidgetproviderinfo. All of this is just laying out the basics so Android is hep to the scene.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.miskatonic.daysold"
    android:versionCode="1"
    android:versionName="1.1" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <receiver android:name="org.miskatonic.daysold.DaysOldWidget" android:exported="false">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"
                android:resource="@xml/daysoldwidgetproviderinfo">
                        </meta-data>
            </receiver>

        </application>

</manifest>

The provider is in res/xml/daysoldwidgetprovider.xml and just looks like this. I think it's saying "I'm this big and I use the widget layout, and I want to be updated every hour, please, or as you call it, every 3600000 milliseonds."

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="72dp"
    android:minHeight="72dp"
    android:updatePeriodMillis="3600000"
    android:initialLayout="@layout/widget">
</appwidget-provider>

And the widget layout is to be found in res/layout/widget.xml. It sets out height and width of the containing box and then the look of the text box inside it. All I'm doing is putting white text on a black background in a square, which is about as simple as it can get. Imagine how complicated it would get laying a program that actually did something!

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout"
    android:orientation="vertical"
    android:layout_width="72dp"
    android:layout_height="72dp"
    android:layout_margin="12dip"
    android:background="@drawable/myshape"
        >

        <TextView
        android:id="@+id/thewidget"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dip"
        android:background="#000000"
        android:text="@string/app_name"
        android:textColor="@android:color/white"
        android:textStyle="bold"
        android:typeface="monospace"
        android:gravity="center_vertical|center_horizontal"
        />

</LinearLayout>

With those files Android knows there's a box on screen and what it's supposed to look like and that it's a widget, but what's actually going on in the widget? Well, just one simple thing, defined in DaysOldWidget.java: figure out today's date and then how many days it's been since my birthday.

package org.miskatonic.daysold;

import java.util.Calendar;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.widget.RemoteViews;
import org.miskatonic.daysold.R;

public class DaysOldWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        RemoteViews remoteViews;
        ComponentName DaysOldW;
        remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
        DaysOldW = new ComponentName(context, DaysOldWidget.class);

        Calendar my_birthday = Calendar.getInstance();
        my_birthday.set(1970,0,1); // Note January is 0 and December is 11.
        Calendar today = Calendar.getInstance();

        long my_birthday_millis = my_birthday.getTimeInMillis();
        long today_millis = today.getTimeInMillis();

        long diff = today_millis - my_birthday_millis;
        long diffDays = diff / (24 * 60 * 60 * 1000);

        remoteViews.setTextViewText(R.id.thewidget, Long.toString(diffDays));
        appWidgetManager.updateAppWidget(DaysOldW, remoteViews );
    }

}

With all that in place, I ran the program in Eclipse (right-click on project name, Run As, Android Application) and the little virtual phone slowly booted up (and once it was running, it stayed running, so retesting was easy). I could long-click on the home screen to add the widget there.

Adding the widget on the virtual phone

It worked! I exported the application (and signed it and so on) and ended up with Days Old.apk, which I copied to my phone and opened up in a file manager. It asked me if I wanted to install the program, and when I did, voila, I could add the widget to my home screen for real.

So now I have what I wanted: a widget on my phone that shows me how many days old I am. The next step is to add a configuration option so that other people can use the widget but enter their own date of birth. There's a way to make an Android widget pop up config options when it's added to the home screen, and if I figure out how to do that and get it working, I'll release the app and the code and try to get it into Google Play.