Ross Thomas and the Library of Congress

Ross Thomas's Wikipedia entry doesn't nearly live up to how good a writer he was. I can't recommend him highly enough. Tony Hiss's 1996 Atlantic article Remembering Ross Thomas is a fine introduction, and this puts it nicely:

There are actually two kinds of escape reading, and only one has to do with snuggling into a book just to take a break from your problems for a few hours. Put down a Thomas book, like a Chandler book, even on a dark night of the soul, and you're never quite back where you were before you began reading. Something, I've always found, has been subtly strengthened and enriched in the meantime. It might be a sense that you can now look awful situations in the eye without dissolving. Or merely a replenished sense that good cheer can still bubble up into the world, and that there's enough on hand for all of us to make it through another season. It's certainly the kind of feeling that again and again makes one glad that Ross Thomas found he had to take up writing.

Ethan Iverson's Ah, Treachery! is a full portrait and describes all his books.

Crime. Politics. Cons. Scams. Schemes. Ross Thomas knew.

I just finished The Brass Go-Between (1969), the first of his short series of books about Philip St. Ives that he wrote under the pseudonym Oliver Bleeck. St. Ives is acting as a middleman between people who stole something from a museum and the museum management who want to pay the ransom. Things go wrong. Late in the book there's this passage, where St. Ives is in Washington and figuring out what's going on.

When they had gone I picked up the green telephone book and looked up a number. I dialed and when it answered, I said, "What time do you close?"

"At ten o'clock," a woman's voice said. "The stacks close at seven forty-five."

I said thank you and hung up and went to the window to see if Fastnaught had told the truth about the the rain. He had so I left my raincoat hanging in the closet, took the elevator down to the lobby, and flagged a cab from the sidewalk. After I was in, the driver turned and gave me a questioning look. He wanted to know where I was headed so I said, "Library of Congress, please."

If you had enough time and enough patience, I suppose you could find out all about everything at the Library of Congress. I spent two hours in its periodical section, guided in my search by an elderly gentleman with a hearing aid who didn't mind scurrying back and forth bearing back issues of some rather esoteric and extremely dull publications. When the periodical room closed at 5:45 I went to the main reading room and spent another hour with the bound back issues of some more tedious publications which looked as if no one had leafed through them in 20 years. When I finished at 7:30 I had acquired a sizable chunk of information and some if it might even prove useful.

It does.

If you want to try Ross Thomas, I'd recommend Briarpatch or Missionary Stew or Out on the Rim to start.

In Fear We Trust 4: LibraryBox

As I said, I'm using a slightly hacked LibraryBox to make a temporary one night only Nuit Blanche special wifi network called InFearWeTrust and web site at infearwetru.st. It's now 4:30 in the morning and Ashley Williamson is running the lights so I can write up what I did.

I followed all the LibraryBox instructions, but I had a problem along the way. I bought the necessary TP-Link MR-3020 and installed PirateBox on it, which was easy enough ... but then I couldn't connect to the wifi network! I don't know why. It just never responded. I set it aside to work on it later and then lost my notes about what network settings I'd used, so I think the router might be dead to me now.

I thought maybe I just had a bum one, so I got another. The same problem happened: I couldn't connect to PirateBox! But then: I could! I can't explain why the problem started or what stopped it. Very mysterious. However, it never came back and everything's worked fine since then.

Connecting to the InFearWeTrust network

After installing LibraryBox I connected to its wifi network by sshing as root to 192.168.1.1. PirateBox and LibraryBox install their files into /opt/piratebox/ and it turned out there are just two files that really mattered to me: /opt/piratebox/bin/droopy and /opt/piratebox/conf/piratebox.conf.

I left the config file alone but for two things:

#HOSTNAME  ... if you really want to change it, plz change:
#    bin/droopy
#    conf/hosts
#HOST="librarybox.lan"
HOST="infearwetru.st"

and

TEXT=""

The TEXT variable defines some stuff that shows on the LibraryBox default page, included in HTML that's inside droopy, so I edited it right there.

droopy is a simple little Python web server ("whose sole purpose is to let others upload files to your computer" but I'm not using that). There's a maintmpl variable that has HTML and includes some other variables. I fiddled it until it did what I needed. I kept a backup every time I did something, because every now and then I'd do something that stopped droopy from running, so I'd have to go back to the previous version and try again. Once or twice it was to do with a % sign, but once I removed a bunch of Javascript I didn't need and that broke it. I didn't bother to dig into it.

There are two ways to add files. The rough way is to power down the box, take out the USB drive, copy files onto it, plug it back in and turn it on. The easier way is to use scp (scp filename.jpg root@192.168.1.1:/opt/piratebox/share/).

In Fear We Trust 3

LibraryBox sitting in the window radiating an InFearWeTrust network. It's right beside where people line up.

Photo of the router in the window

I'm working the lighting board. The two sheets of paper on the left are the Q (as I'm told it's called---I've never worked a lighting board before, but this show has a simple lighting design).

The lighting board

In Fear We Trust 2

A post-rehearsal update. This is the sign people will see outside tomorrow night.

And when they connect to infearwetru.st they will see (mind you, this will change):

In Fear We Trust 1

Saturday night to Sunday morning I'm going to be part of a Nuit Blanche happening at the Arts and Letters Club. Andrew Sookrah is running a show called "In Fear We Trust," a fourteen-minute multimedia performance with dancers, music, video (projected on the dancers) and cereal boxes. It'll be performed about 18 times through the night, from sunset to sunrise.

In Fear We Trust hacked LibraryBox

My part of it is running a one-night-only Nuit-Blanche-specific temporary semi-autonomous wifi network called InFearWeTrust. It will only be accessible at the club that night. I'm running it on a TP-Link MR-3020 router with a hacked version of Jason Griffey's LibraryBox. We'll be feeding out music, video, pictures, drawings, schematics, ambient audio recordings and anything else we have at hand. If you're at the club you can connect to that wifi network and get stuff that's not on the open web. Come the dawn, the network will disappear, never to reappear.

I'll post more about later. If you're in Toronto and out for Nuit Blanche this Saturday night, come by. I haven't seen a full rehearsal yet but from what I know it's going to be good. If you're on Twitter, use #snbto and #infearwetrust, and if you CC license a photo I'll add it to the network.

Counting and aggregating in R

A short post about counting and aggregating in R, because I learned a couple of things while improving the work I did earlier in the year about analyzing reference desk statistics. I'll post about that soon.

I often want to count things in data frames. For example, let's say my antimatter equivalent Llib and I have been drinking some repetitive yet oddly priced beverages:

> bevs <- data.frame(cbind(name = c("Bill", "Llib"), drink = c("coffee", "tea", "cocoa", "water"), cost = seq(1:8)))
> bevs$cost <- as.integer(bevs$cost)
> bevs
  name  drink cost
1 Bill coffee    1
2 Llib    tea    2
3 Bill  cocoa    3
4 Llib  water    4
5 Bill coffee    5
6 Llib    tea    6
7 Bill  cocoa    7
8 Llib  water    8

(Note how I specified two names and four drinks, but they repeated themselves to fill up the eight lines to equal the size of the cost sequence I specified. R does that automatically and it's very useful.)

How many times does each name occur? That's just basic counting, which is easy with the count function from Hadley Wickham's excellent plyr package. Now, like a lot of R functions, the count help page is a bit intimidating.

> library(plyr)
> ?count

The help page says:

count                   package:plyr                   R Documentation

Count the number of occurences.

Description:

     Equivalent to ‘as.data.frame(table(x))’, but does not include
     combinations with zero counts.

Usage:

       count(df, vars = NULL, wt_var = NULL)

...

Hmm! But there are examples at the bottom, and like all R documentation, you can just run then and look at what happens, which will probably explain everything. And here are more. How many times does each name occur?

> count(bevs, "name") 
  name freq
1 Bill    4
2 Llib    4

How many times did each person drink each drink? To say you want to tally things up by more than one column use the c function to combine things into a vector:

> count(bevs, c("name", "drink")) 
  name  drink freq
1 Bill  cocoa    2
2 Bill coffee    2
3 Llib    tea    2
4 Llib  water    2

It's all pretty easy. Just tell count which data frame you're using, then which columns you want to tally by, and it does the counting very quickly and efficiently.

How much did I spend in total? How much did I spend on each drink? aggregate does the job for this kind of figuring.

aggregate                package:stats                 R Documentation

Compute Summary Statistics of Data Subsets

Description:

     Splits the data into subsets, computes summary statistics for
     each, and returns the result in a convenient form.

Again, there are good examples at the end of the help file. But for my example, first let's see how much Llib and I have spent on each kind of drink:

> aggregate(cost ~ name + drink, data = bevs, sum)
  name  drink cost
1 Bill  cocoa   10
2 Bill coffee    6
3 Llib    tea    8
4 Llib  water   12

That command says "I want to apply the sum function to the cost column while aggregating rows based on unique values in the name and drink columns."

How much did we each spend total? Forgot about aggregating by drink, and just aggregate by name:

> aggregate(cost ~ name, data = bevs, sum)
  name cost
1 Bill   16
2 Llib   20

What was the mean price we paid? Change sum to mean:

> aggregate(cost ~ name, data = bevs, mean)
  name cost
1 Bill    4
2 Llib    5

When will Firefox overtake Emacs?

The other day my laptop told me Firefox wanted to be updated to version 15.01. Go ahead, I said, and a few minutes later it was done. Firefox is updating itself all the time now, a new one major release every six weeks, which let me tell you doesn't fit well with the way public-access computers are managed at the library where I work. The reference desk computer was still using version 3 or 6 or something until they had to update it because of that Java bug.

Speaking of version numbers, I'm waiting for Ubuntu 12.10 to come out in October because it will have Emacs 24.2 on it, the latest version of my favourite editor. I'm running 23.3 right now but there are a few things in 24 that I want to use, though I can't be bothered to compile it from source or use a version from some dude's repository.

Firefox 15 ... Emacs 24 ... Firefox is a fairly new program, but Emacs has been around since the 1970s ... when will Firefox overtake Emacs? This sounded like a job for R.

First, using Wikipedia's Firefox release history and Emacs pages, and Jamie Zawinski's Emacs timeline, I put together a CSV file of versions and dates. Then I fired up R and ran the following:

> library(ggplot2)
> programs <- read.csv("http://www.miskatonic.org/files/se-program-versions.csv")
> programs$Date <- as.Date(programs$Date, format="%B %d, %Y")
> head(programs)
  Program Version       Date
1   Emacs    24.1 2012-06-10
2   Emacs    23.4 2012-01-29
3   Emacs    23.3 2011-03-10
4   Emacs    23.2 2010-05-08
5   Emacs    23.1 2009-07-29
6   Emacs    22.3 2008-09-05
> head(subset(programs, Program == "Firefox"))
   Program Version       Date
18 Firefox      16 2012-10-09
19 Firefox      15 2012-08-28
20 Firefox      14 2012-06-26
21 Firefox      13 2012-06-15
22 Firefox      12 2012-04-24
23 Firefox      11 2012-03-13
> ggplot(programs, aes(y = Version, x = Date, colour = Program)) 
  + geom_point() 
  + geom_smooth(span = 0.5, fill = NA)

That looks like this:

Emacs and Firefox version number timeline

But when will the line meet? When will Firefox overtake Emacs? I had no idea how to do this so I asked on Cross Validated, the statistics Stack Exchange site: How to predict or extend regression lines in ggplot2?

As you can see, I got an answer, and with a bit of fiddling and leaving out early Firefox versions before they went on their rigid schedule I did this:

ggplot(subset(programs, !(Program == "Firefox" & Version < 4)), 
  aes(y = Version, x = Date, colour = Program)) 
  + geom_point() 
  + ylim(0,30) 
  + xlim(as.Date("1985-01-01"), as.Date("2015-01-01")) 
  + stat_smooth(method = lm, fullrange = TRUE)

which gives this chart:

Emacs and Firefox version number timeline with prediction

I don't know how to extract the range where the lines collide, but it looks like in late 2013 Firefox will overtake Emacs with a version number at or under 25. You heard it here first.

Review of The History of the Library in Western Civilization: From Cassiodorus to Furnival: Classical and Christian Letters, Schools and Libraries in the Monasteries and Universities, Western Book Centres

(I had a book review published in the fall 2011 issue of Papers of the Bibliographc Society of Canada (vol. 49, no. 2) (table of contents). Here is my original text, which was slightly edited.)

Konstantinos Sp. Staikos, The History of the Library in Western Civilization: From Cassiodorus to Furnival: Classical and Christian Letters, Schools and Libraries in the Monasteries and Universities, Western Book Centres. Trans. Timothy Cullen and Doolie Sloman. New Castle, DE: Oak Knoll Press; Houten, NL: HES & De Graaf, 2010. 500 pp.; US $75.00 ISBN 9781584561811

This is the fourth volume in a series of six. The first three were reviewed in these pages and comments there apply equally well here, and will also, I expect, to the fifth, due in 2012, subtitled "The Renaissance: From Petrarch to Michelangelo." (The sixth will be a bibliography and index.) Volume one, "From Minos to Cleopatra," was reviewed by Merrill Distad in the Papers/Cahiers 44 [Spring 2006]: 134-136; volume two, "From Cicero to Hadrian," by Tana J. Allen in 45 [Fall 2007]: 215-217; and volume three, "From Constantine the Great to Cardinal Bessarion," by Peter F. McNally in 46 [Spring 2008]: 116-119.

The word for this book is "lavish." It is "a lavish production with excellent images and easy-on-the-eyes typeface" (Allen) and "lavishly illustrated" (McNally) with over 170 "black-and-white and colour illustrations contained in a lavishly produced format" (Distad).

The first chapter begins with Rome and Italy after Constantine had moved the capital and in a few pages gives an overview of the rest of the book: the growth of Christian writing and scholarship, scriptoria, the crucial role of monks and monasteries, the Carolingian Renaissance, and the rise of universities. The preservation of classical literature through medieval times is where libraries, such as they were, played a key role, and four important Christian scholars involved in this were Boethius, Cassiodorus, Isadore of Seville, and the Venerable Bede. Tertullian (b. 150) begins a discussion of early Christian writers in Latin.

Chapter 2 covers monastic libraries and changes in book production and distribution. St. Jerome's work process illustrates the practices of the times: he dictated to a notarius and the text was given to a trained and educated scribe who would make an examplar that would be used by copyists in a scriptorium as the basis for more copies. St. Augustine's personal library is examined (one of many such in the book), and there is discussion of the Bible—so important that the scribe's work of copying was an apostolic task—and of manuscripts that survived the start of the Early Middle Ages. The chapter ends with the Vivarium, a monastery founded in 538 by Cassiodorus, who "foresaw that with the collapse of political institutions monasteries would play an important part in preserving the Graeco-Roman tradition."

Chapter 3, "Roman and Early Medieval Britain," oddly covers nothing about Roman Britain. Monasteries in Britain and Ireland were important in keeping classical knowledge alive, and libraries there became the best in Europe. Some of the best evidence comes through Bede at Jarrow where "there was no comparable collection in continental or insular Europe."

Chapter 4 is about the Carolingian Renaissance. Englishman Alcuin, who met Charlemagne on a trip to Italy and later joined his court in Aachen, revived the teaching of the seven liberal arts and played a crucial role in saving manuscripts and building libraries. Charlemagne himself built a large library of his own. The chapter also looks at the major monastic libraries of the period, such as St. Martin's and Corbie Abbey in France and St. Gallen in Switzerland.

As universities began to appear from the twelfth century, and education moved out of the control of monasteries and eventually away from the Church, so too did books and libraries. An early textbook system arose: peciae were chapters of books used as the basis of study; they were written by teachers, then copied, sold, rented, and passed on from student to student. The library of the Sorbonne is discussed in some detail: in 1272 it received a donation of 300 books that doubled its size. In 1286 the books were split into those that circulated and those that were chained. By the mid-1300s the library had 1,720 books, over 300 of them chained: it was the largest university library extant.

Chapter 7 closes the middle ages by looking at a number of individual libraries, beginning with Richard de Bury, an English bishop and diplomat whose Philobiblion is "an essay recording the paths and methods he pursued for the composition of his library ... nothing was to stand in the way of his enriching his library, not even the financial factor." The libraries of Charles V of France, Simon de Plumetot, and Hereford Cathedral are discussed in detail, as are the papal libraries in Avignon and Rome. (The chapter ends with an unrelated but charming and curious section that stands outside the rest of the book, a paean to “the book guardian” and “the lord of the library” from classical times to the Renaissance that will warm the heart of any librarian.)

The final chapter is about architecture, and Staikos, an architect, clearly enjoys it. It is well illustrated with drawings of cupboards and bookcases, plans, diagrams and photographs.

How well does the book cover its subject? McNally's evaluation holds: "To the question—for whom will this book be of value?—there is no easy answer. Scholars will appreciate the large amounts of literary evidence from many languages. Unease will, however, be felt at the loose interpretive structure. General readers will be fascinated by the wealth of illustrations but overwhelmed by the detail and lack of a compelling narrative.... [T]he overall constructions lacks a firm design.... Serious library historians will wrestle with the remaining chapters, alternately thankful for and frustrated by the lush and unruly detail."

There are a number of typographical errors (e.g. "expanses pf water" on p. 109, "objects of virtu" on p. 144), but worse are sentences that border on the incomprehensible, such as one beginning "While remembering and to remind of persons who undertook and performed the duties of library superintendents at this point" (p. 363). In chapter eight, footnote 49 reads, "Regarding the abbey of Cluny, see pp. 000-000." In the index, readers will be confused that Richard de Bury and Richard de Furnival are under R, not D, or B and F, but Peter Abelard is listed as "Abelard Peter."

As Distad said, "Staikos's earlier book, The Great Libraries: From Antiquity to the Renaissance (3000 B.C. to A.D. 1600), appeared in 2000 in a larger but equally sumptuous volume.... A comparison of the two elicits a feeling of déjà vu that extends beyond the format to nearly all of the illustrations, as well as large chunks of text, which, though, slightly rearranged and with minimal rephrasing, are virtually the same in both works." The curious reader is advised to start with it and Scribes and Scholars: A Guide to the Transmission of Greek and Latin Literature by L.D. Reynolds and N.G. Wilson (3rd ed., 2001).

William Denton (York University, Toronto)

Making the Days Old widget

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.

My first experiences with a Nexus 7

I bought a Nexus 7, a small tablet from Google and Asus running Android 4.1. Here are a few notes about it from the point of view of an Ubuntu user.

Here's what I do when I get something like this:

  • Plug it in.
  • Copy a Doctor Who video onto it.
  • Watch the video.

None of this worked for me right off the bat, partly due to me (from ignorance) and partly due to Google and Asus (from I don't know what).

I ran FreeBSD for years, until a chain of events led me from my old window manager to GNOME and then to trying Ubuntu and then switching to it. The moment I'll never forget was when I plugged my printer into my test Ubuntu box. With FreeBSD I'd spent an entire day configuring the thing and even then I couldn't get it working with the USB cable so I had to use the parallel port. With Ubuntu, I plugged in the USB cable and it said, "I see you've plugged in a Brother HL-2040 printer. Would you like to print a test page?" Yes I would! And it worked. Just like normal people, who run Windows or a Mac. And when I plugged in a USB drive it automatically mounted it! I didn't have to watch /var/log/syslog and figure out what device to mount by hand.

Perhaps I'm spoiled now, but when I plug something in, I expect things to happen. With the Nexus 7, I plugged it in and nothing happened.

Turns out it's because Android doesn't support USB any more so you need to use Media Transfer Protocol, which is fine if you run Windows, but I don't, and I didn't know anything about this until David Fiander told me. It did say something about being a media device, I admit, but I just ignored that. However, the instructions at Connect your Android Ice Cream Sandwich Phone to Ubuntu for File Access are complete and helpful (though I used /media/Nexus7 as the mount point), but the fact anything like this might be necessary is embarrassing for Ubuntu.

Other options:

  • AirDroid, an app that lets you transfer files by HTTP.
  • ADB, the Android Debug Bridge, as Dan Scott pointed out. adb push will copy files.
  • gMTP, a file manager/media client for MTP-mounted devices.

Copying a Doctor Who video over: I tried all the options except ADB, and I'll from now on I'll either mount the file system by hand or use gMTP. I used both to copy a video over and they were fine.

Watching the video: This was a much bigger problem than I'd expected. Out of the box, the Nexus 7 doesn't play AVI files! I was very surprised. The previous release of Android did. Why doesn't this? I know video containers and encodings are complicated, but I expect Google to take care of things like that for me. I installed MoboPlayer and MV Player, but they couldn't handle the AVIs. MoboPlayer suggested some codecs it wanted to download, but it couldn't find them in the Play Store and choked on downloading them from its own site.

Finally I installed VPlayer and bought a codec package, using the $25 in Play Store money that came with the device. Now AVIs play and there's no annoying nagware.

This is a sad and embarrassing solution. There should be some FLOSS package that does this, and if you know how to get one working, let me know.

Other bad points:

  • There's no SD card.
  • There's no camera on the back.
  • I can't do voice input into Wolfram Alpha.

Good points:

  • It's a very nice size and it's light but strong.
  • It's very fast.
  • The voice search and Google Now are impressive, though Apple's doing well with Siri being able to use Wolfram Alpha.

Knowing what I know now, would I recommend it to anyone who already owns something like an Asus Transformer Prime? Probably not. Wait for it to upgrade to Android 4.1, and carry it around in a bag or case instead of your jacket pocket.

Syndicate content