Archive

Posts Tagged ‘android’

Why You’ll (Rarely) Catch Me With a Printed Book

January 25, 2014 2 comments

E-reader on top of books

kodumut via Flickr under Creative Commons license

I love books. I love book stores. I worked in a library for four years. But when I read Ali Wunderman’s post entitled Why You’ll Never Catch Me With An E-Reader, I was not convinced. In almost all cases I prefer to buy and read digitally. I’ll discuss her argument, my reasons for preferring digital in most cases, and cases where e-readers and digital books are worse than their paper alternatives.

The two main points of Ms. Wunderman’s argument are:

  • She likes the sensations of reading a physical book (touch, smell, sight)
  • She values the serendipity of meeting new friends who love the book that she’s reading. Had she used an e-reader, those people would not have been able to see what she was reading, and thus she would have missed out on such encounters

I can’t argue against the first point – it’s a matter of opinion whether or not holding a book feels good. Since e-readers are such a recent invention, I think this argument is rooted in nostalgia more than anything else. It would be interesting to see whether children who grow up with a choice between e-readers and physical books end up with such a physical attachment to books. I do like the touch and smell of books, but it’s not enough to make me buy paperbacks exclusively.

The second point is also subjective. I’ve never had strangers comment on what I’m reading, but I can imagine it would be a fun experience. I am willing to bet that it’s rare. On the other hand, I have heard that some women are more comfortable reading romance novels on their e-readers than physical copies. I don’t really care one way or another; I don’t read books with the intention of showing others what I’m reading.

I have had the experience of bonding with new friends over the contents of our respective bookshelves. If there were no books for us to look at, we would have missed out on some level of connection. While this makes more sense to me than the serendipity argument, I still don’t think this is a reason to stay slavishly attached to dead trees. Once you have established a relationship, it’s easy to converse about books you’ve read, no matter the medium.

Digital preference

The reasons I prefer digital books are price, convenience, ergonomics and lack of physical clutter.

Price

E-books are often cheaper than physical alternatives. This makes sense – the price of publishing and distribution is virtually zero.

Convenience

E-books are incredibly convenient. Let me count the ways:

  • Instant gratification – purchase, download, and start reading a book in less than a minute. No need to wait for a book to be shipped to you, or to go to a store
  • Instant definitions – no need to break the flow of reading to learn the meaning of a word. Tap and hold on the word to get a quick pop-up definition
  • Read free samples of a book before committing to buying it
  • Free lending library – check out one free book a month to read

Press on a word for a second and get a definition. No need to leave the book. Kindle App on Android

There’s a saying that the best camera is the one you have with you. It’s the same with books. I rarely bring physical books with me, sometimes I have my Kindle Paperwhite, but I always have my phone with me, and that phone has all of my Kindle purchases on it. When I used to use an iPhone it was uncomfortable to read on such a small screen, but I recently switched to a Nexus 5 and have happily consumed entire books on it. The progress I make on one device is instantly synced with all of my other Kindle compatible devices.

Ergonomics

I find it more pleasant to read digitally. I can control how big the font is, I can turn pages one-handed, I can read in the dark with no external illumination, and my devices are light to hold. For example, I bought Cryptonomicon on Kindle to replace a hardcover version partly because I was tired of reading such a bulky book.

Clutter-free

Most importantly, buying digitally frees me from physical clutter. If you’ve ever moved, you know how heavy and unwieldy books are. If you’re traveling, they add weight and bulk to your luggage.

“Rarely”

Unlike Ms. Wunderman, I am not absolute in my preference. I often prefer digital, but I acknowledge that there are some real problems with digital books. These include ownership, longevity, batteries, screens, and the distractions of reading digitally.

Ownership

When you buy digital content, what do you actually own? In 2009, Amazon deleted unauthorized versions of Animal Farm and 1984 straight off of owners’ Kindles. As the article says,

Digital books bought for the Kindle are sent to it over a wireless network. Amazon can also use that network to synchronize electronic books between devices — and apparently to make them vanish.

When you deal with DRM (digital rights management) content, there are very strong restrictions placed on what you can and can’t do with the content. It’s more like a limited license to view the content rather than outright ownership.

For instance, let’s look at lending. With physical books, you can lend your book to whomever you want for as long as you want. With Amazon’s titles, not all publishers allow digital lending in the first place. Of those that do, there are Draconian limitations. From the Amazon Kindle help page:

You can lend a Kindle book to another reader for up to 14 days… A book can only be loaned one time.

Until you can freely loan or give away your digital copies of books, paper wins hands down.

Longevity

Even if you buy DRM-free content, you take the chance that you won’t be able to read that content in a few years or decades. There is a strong precedent of technologies dying and data being trapped on obsolete devices; see Lost Formats for examples. If Amazon goes out of business, what happens to all of the Kindle content you’ve amassed? Hopefully if that were to happen, Amazon would offer a service like Google Takeout to transfer the books to you. (Full disclosure: I work for Google.)

A floppy disk - one of many dead storage formats

Trav1085 via Wikipedia

The other aspect of longevity is the e-readers themselves. I haven’t had the best of luck with my Kindles so far – I am on my fourth Kindle in about as many years. Three of them died quickly, but as of yet I’ve had no problems in the past 2.5 years with the Paperwhite. It makes me wonder how long these devices last. If you have to buy a new $100 device every 3-5 years, this changes the calculus of whether e-books are more affordable.

Batteries

Digital readers run out of batteries; books don’t. Standalone e-readers typically don’t need charging very often, but phones do. If I were traveling and didn’t have ready access to electricity, this would be a concern. In practice this isn’t a big problem for me.

Screens

Some people prefer the look of a book to an e-reader screen. The e-ink display on the Kindle has improved with each generation, both in resolution, sharpness, and refresh speed. I don’t think there’s an objective winner here. The display on my Nexus 5 is incredibly sharp and I can read it in the dark, just as with the Paperwhite. My only objection to the screen is that it contributes to my spending 90% of my time staring at a glowing rectangle. I mitigate this somewhat by changing to the white text on black background on my phone, and keeping the light low on the Paperwhite.

Distractions

It’s easy to get distracted if you are reading digital books on a multi-purpose device. Reading takes time and concentration. Sometimes it’s hard to stick with that when there’s the allure of games and an infinite expanse of Internet content that’s a few button presses away.

Standalone e-readers offer a more focused reading experience that’s closer to that of reading a book. Since there’s fewer things you can do on it, there’s less temptation to do something other than read. Depending on your level of willpower, this point could be completely moot.

Conclusion

I vividly remember seeing the first clunky version of the Kindle just a few years ago and wondering how its owner could enjoy reading on it. The technology has improved so much since then that I’m a happy convert. While I acknowledge the superiority of physical books in some ways, it often makes sense to buy digital. Doing so avoids physical clutter and is extremely convenient. Most of the technical problems with digital books and e-readers have been solved; the remaining hurdles of consumer-unfriendliness are sociological problems that we can combat. For example, Microsoft changed its restrictive DRM in the Xbox One due to overwhelming negative response. If consumers showed as much passion for their rights to the book publishers and Amazon, perhaps we’d see a loosening of the reins as well.

Advertisements

Android – disappearing emulator ? Restart adb server

September 13, 2010 12 comments
While developing for the Android platform has gotten a lot better in the past year, there are still some rough edges.  In particular, emulators sometimes disappear from the list of attached devices (accessed via adb devices from the terminal, or within the Devices tab when in DDMS view in the Eclipse plugin).  When this happens, you cannot interact with it via the terminal or Eclipse.  That’s a pretty big problem.


A screen shot illustrating a running emulator that does not appear in the list of attached devices

An emulator within the devices window

To solve this, you should take the following steps:


# Device is running but not showing up
[497][nicholasdunn: /Users/nicholasdunn]$ adb devices
List of devices attached

# Kill and restart
[498][nicholasdunn: /Users/nicholasdunn]$ adb kill-server
[499][nicholasdunn: /Users/nicholasdunn]$ adb start-server
* daemon not running. starting it now *
* daemon started successfully *

# Device appears, but is listed as offline
[500][nicholasdunn: /Users/nicholasdunn]$ adb devices
List of devices attached
emulator-5554    offline

# One more invocation of adb devices should get it recognized
[501][nicholasdunn: /Users/nicholasdunn]$ adb devices
List of devices attached
emulator-5554    device

If this happens to you frequently (it does to me), you can create an alias within your .bash_profile file (~/.bash_profile):

alias adb-restart='adb kill-server; adb start-server; adb devices; adb devices'

Reload your .bash_profile file:

source ~/.bash_profile

You can then invoke it from the terminal by typing adb-restart.  Sometimes one invocation of adb devices is enough to have the emulator show up as a device; others requires two.  Not sure why that is.  To be safe I’m including two in the script.

Android: Principle of least surprise violation in GPS emulation

July 16, 2010 1 comment

Some of the most viewed posts on this blog have been about Android development, namely some workarounds for bizarre bugs that Google *still* has not fixed, months later.  I haven’t touched Android in a few months, but recently have started to dive back in.  Here’s yet another instance where an Android related piece of software performs completely counter-intuitively.

When developing an Android application, I often use the emulator rather than an actual hardware device.  In order to test GPS related functionality, you can either send simulated GPS events through the Dalvik Debug Monitor Service (DDMS) via Eclipse or through the Android terminal via telnet.  I’ll detail surprising bugs/defects in both ways.

DDMS

The DDMS Eclipse view is available after installing the ADT plugin, and it’s absolutely crucial in order to see logging messages produced from your application (“LogCat”).    It also allows you to send GPS coordinates, as the following screenshot shows.

Unfortunately there is a bug in this software that drove me up the wall, and I never would have figured it out myself.  I was running into the problem where the first GPS coordinate sent would be received by the emulator, but all subsequent ones would not.  It turns out the problem has to do with locales and decimals being treated as commas, or vice versa.  See the following Google bug report for more details.  The bug has been open over a year; fortunately it looks like it’s slated to be fixed in the next SDK release.

Telnet

If you telnet into your emulator instance, you have access to the Android console environment.

telnet localhost 5554

Android Console: type ‘help’ for a list of commands
OK

One of the commands you can send is fix, which send a latitude longitude pair to the emulator.  Well, that’s what I assumed it did.  After wondering why all my locations were were coming out incorrectly, I read the documentation a little more carefully.

fix <longitude> <latitude> [<altitude>] Send a simple GPS fix to the emulator instance. Specify longitude and latitude in decimal degrees. Specify altitude in meters.

There is a VERY strong convention to specify latitude before longitude.  (“latitude and longitude” returns 4,240,000 results as opposed to 542,000 for “longitude and latitude”)  Having this command take arguments in the opposite order is very surprising and counter-intuitive.   I notice that the DDMS screen has it longitude, latitude as well, but even within the Android APIs, you specify the latitude first!

I realize I should have read the documentation first, but it still is a flaw that the software behaves in unexpected manners (it violates the Principle of Least Surprise/Astonishment), and inconsistent with how API calls in the rest of the OS work.  If someone has an explanation for why it’s written this way, I’d love to hear it.

Edit: Later I learned that KML has the same convention, longitude followed by latitude. This corresponds with the x,y ordering conventional in math, so it’s perhaps not so surprising after all. This ordering problem is fairly minor in the grand scheme of things, and reflects more on my own inexperience with geospatial APIs than any fault of Android itself.

Android: Dialog box tutorial

October 31, 2009 23 comments

Hi folks,

There’s a lot that’s been written about the evils of dialog boxes , those little boxes that pop up to ask you how whether you’re sure you want to do action X, or provide some information Y.  The real issue with most of these dialog boxes is that they block the user interface, stealing the attention of the user, and disallowing him to deal with what he was doing until he deals with this interruption.

I agree that they are overused and often can be eliminated entirely.  For instance, instead of popping up a dialog box asking whether you’re sure you want to delete something, just do the deletion and provide a mechanism for them to undo their mistake.  Gmail does this well; the problem is that in general it’s much more difficult to provide an undo mechanism than it is to shove a dialog in the user’s face, make him choose an option he might not fully understand, and then absolve oneself of the consequences when he deletes something he didn’t intend to.

Philosophical debate aside, it’s still useful to be able to use a dialog box in a pinch.  I will step through the code to illustrate how to do so in Android.

Android provides a useful Builder implementation for Dialogs.  I’ll write a post about the niceness of the Builder design pattern eventually, but suffice to say, they allow a nice mechanism for specifying optional arguments rather than having dozens of overloaded constructors that take in various arguments.  When all the arguments have been provided to the Builder through a series of chained method invocations, you convert the Builder object into the object it creates through a final create() command.

The class used to create dialog windows is AlertDialog while the builder object I mentioned earlier is AlertDialog.Builder .  Because the dialog will be displayed on the screen, we need to provide the builder object with the Context in which it should render itself.

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(true);
builder.setIcon(R.drawable.dialog_question);
builder.setTitle("Title");
builder.setInverseBackgroundForced(true);
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
  @Override
  public void onClick(DialogInterface dialog, int which) {
    dialog.dismiss();
  }
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
  @Override
  public void onClick(DialogInterface dialog, int which) {
    dialog.dismiss();
  }
});
AlertDialog alert = builder.create();
alert.show();

This is the basic code you need to create a dialog box that pops up, has yes and no option, and dismisses itself gracefully when either of those two options are clicked.

Now, if you’re used to Java’s swing dialog boxes, you might be surprised to see the listeners attached to the buttons in the setNegativeButton and setPositiveButton methods  In Swing, the dialog blocks all other tasks until the dialog is dismissed; as such it’s sufficient to check the return code of the dialog, and then take action based on that value.  That doesn’t fit into the Android paradigm, however, because at all times the app must be responsive and able to be interrupted by an incoming phone call or text message, for instance.  Thus the need for the asynchronous button listeners.

This complicates things somewhat; how do I perform business logic within the context of the dialog?  How do I get handles to the object or objects on which I need to perform actions?  Unlike some other languages, one cannot pass methods as objects in Java.  As such, we should use a Function Object to the dialog, said object encapsulating the business logic that must happen when a button is pressed.

I choose to implement my function object with the Command design pattern; the source of my Command interface is as follows:

/**
 * Functor object that allows us to execute arbitrary code.
 *
 * This is used in conjunction with dialog boxes to allow us to execute any
 * actions we like when a button is pressed in a dialog box (dialog boxes
 * are no longer blocking, meaning we need to register listeners for the various
 * buttons of the dialog instead of waiting for the result)
 *
 * @author NDUNN
 *
 */
public interface Command {
	public void execute();

	public static final Command NO_OP = new Command() { public void execute() {} };
}

I provide a wrapper around the Command object in order to make the API easier to make the dialog boxes.

public static class CommandWrapper implements DialogInterface.OnClickListener {
  private Command command;
  public CommandWrapper(Command command) {
    this.command = command;
  }

  @Override
  public void onClick(DialogInterface dialog, int which) {
    dialog.dismiss();
    command.execute();
  }
}

Combining this with a variation of the earlier code, we have a general purpose method that returns a Dialog confirming whether we want to delete something.

private static final CommandWrapper DISMISS = new CommandWrapper(Command.NO_OP);

public static AlertDialog createDeletionDialog(final Context context,
    final String name, final Command deleteCommand) {

  AlertDialog.Builder builder = new AlertDialog.Builder(context);
  builder.setCancelable(true);
  builder.setIcon(R.drawable.dialog_question);
  builder.setTitle("Are you sure you want to delete \"" + name + "\"?");
  builder.setInverseBackgroundForced(true);
  builder.setPositiveButton("Yes", new CommandWrapper(deleteCommand));
  builder.setNegativeButton("No", DISMISS);
  return builder.create();
}

Here is this method used in context:

// When the delete button is clicked, a dialog pops up confirming
deleteButton.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    Command delete = new Command() {
      public void execute() {
        deleteFromDatabase(personID);
      }
    };
    AlertDialog deletionDialog = DialogUtils.createDeletionDialog(ShowPlaceActivity.this, personName, delete);
    deletionDialog.show();
  }
});

In conclusion, you’ve seen how Android provides a nice Builder interface for creating dialogs, and how these dialogs differ from those of Java. Furthermore, you’ve seen how the Command design pattern can encapsulate business logic and be executed the instant a button is pressed.

Categories: Android, regular Tags: , ,

Android – ItemizedOverlay + ArrayIndexOutOfBoundsException / NullPointerException workarounds

October 19, 2009 73 comments

There’s some gotchas to be aware of when using the ItemizedOverlay class Google provides.

Here’s the situation (a fairly common one): You have a dynamic list of Icons you wish to display on a map.  The list can grow and shrink.  You might think the following would work:

public class CustomItemizedOverlay extends ItemizedOverlay {

	private ArrayList mOverlays = new ArrayList();

	public CustomItemizedOverlay(Drawable defaultMarker, Context mContext, MapView view) {
		super(boundCenterBottom(defaultMarker));
	}

	@Override
	protected COINOverlayItem createItem(int i) {
		return mOverlays.get(i);
	}


	@Override
	public int size() {
		return mOverlays.size();
	}

	public void addOverlay(COINOverlayItem overlay) {
		mOverlays.add(overlay);
		populate();
	}


	public void clear() {
		mOverlays.clear();
		populate();
	}
}

Seems like a reasonable assumption, doesn’t it?  If you try to use this class, you will immediately see a problem – if you do not add any items to the list before it is displayed, you will get a NullPointerException.  This is well documented in two separate threads; Google has marked the issue as Won’tFix.  The workaround is to call populate() within the constructor.  OK, that’s fine.  Our new constructor looks like this:

public CustomItemizedOverlay(Drawable defaultMarker, Context mContext, MapView view) {
		super(boundCenterBottom(defaultMarker));

	        // Workaround for null pointer exception with empty list
		// <a href="http://osdir.com/ml/AndroidDevelopers/2009-08/msg01605.html">http://osdir.com/ml/AndroidDevelopers/2009-08/msg01605.html</a>
		// <a href="http://code.google.com/p/android/issues/detail?id=2035">http://code.google.com/p/android/issues/detail?id=2035</a>
		populate();
	}

You’re not out of the woods yet, though. Say you have clicked on one of the icons on the map. Now say you somehow change your list of icons, e.g. by clearing all of them off the map. Click on the same point where the icon USED to be, and you’ll be faced with a nasty stack trace:

10-19 11:02:48.009: ERROR/AndroidRuntime(1375): java.lang.ArrayIndexOutOfBoundsException
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at com.google.android.maps.ItemizedOverlay.maskHelper(ItemizedOverlay.java:562)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at com.google.android.maps.ItemizedOverlay.setFocus(ItemizedOverlay.java:365)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at com.google.android.maps.ItemizedOverlay.focus(ItemizedOverlay.java:539)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at com.google.android.maps.ItemizedOverlay.onTap(ItemizedOverlay.java:455)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at org.mitre.coin.map.COINItemizedOverlay.onTap(COINItemizedOverlay.java:63)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at com.google.android.maps.OverlayBundle.onTap(OverlayBundle.java:83)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at com.google.android.maps.MapView$1.onSingleTapUp(MapView.java:346)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at android.view.GestureDetector.onTouchEvent(GestureDetector.java:503)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at com.google.android.maps.MapView.onTouchEvent(MapView.java:623)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at org.mitre.coin.view.ModifiedMapView.onTouchEvent(ModifiedMapView.java:26)
10-19 11:02:48.009: ERROR/AndroidRuntime(1375):     at android.view.View.dispatchTouchEvent(View.java:3368)

The source is closed, so you have no idea what is throwing the ArrayIndexOutOfBounds; it’s in code you didn’t write. Once again, there’s a workaround for it, but it’s certainly not intuitive, nor is it mentioned in the documentation. You must called setLastFocusedIndex(-1); whenever you structurally modify the list, but before you call populate(). The issue is documented in more detail here.

The skeleton now looks like the following:

public class CustomItemizedOverlay extends ItemizedOverlay {

	private ArrayList mOverlays = new ArrayList();

	public CustomItemizedOverlay(Drawable defaultMarker, Context mContext, MapView view) {
		super(boundCenterBottom(defaultMarker));

		// Workaround for bug that Google refuses to fix:
		// <a href="http://osdir.com/ml/AndroidDevelopers/2009-08/msg01605.html">http://osdir.com/ml/AndroidDevelopers/2009-08/msg01605.html</a>
		// <a href="http://code.google.com/p/android/issues/detail?id=2035">http://code.google.com/p/android/issues/detail?id=2035</a>
		populate();
	}

	@Override
	protected COINOverlayItem createItem(int i) {
		return mOverlays.get(i);
	}


	@Override
	public int size() {
		return mOverlays.size();
	}

	public void addOverlay(COINOverlayItem overlay) {
		mOverlays.add(overlay);
		setLastFocusedIndex(-1);
		populate();
	}


	public void clear() {
		mOverlays.clear();
		mapView.removeAllViews();
		numItems = 0;
		// Workaround for another issue with this class:
		// <a href="http://groups.google.com/group/android-developers/browse_thread/thread/38b11314e34714c3">http://groups.google.com/group/android-developers/browse_thread/thread/38b11314e34714c3</a>
		setLastFocusedIndex(-1);
		populate();
	}
}

Hopefully this pops up in Google and saves some people some trouble.

Categories: Android, Java, programming Tags:

Android – OverlayItem.setMarker(Drawable icon)

October 16, 2009 37 comments

I’ve been developing in Android for a little over two months off and on and am finding certain things I really love and certain things I hate.  This is one of the things I hate.

Android provides a nice class to manage drawing OverlayItems to a map called ItemizedOverlay; for instance you might want to display all the people in your contact list on a map.  Well, you’d probably want to display a different icon for each person, right?  OK, examine the API.

setMarker

public void setMarker(android.graphics.drawable.Drawable marker)

Sets the marker to be used when drawing this item on the map. Setting the marker to null will cause the default marker to be drawn (the marker is null by default, so you can just skip this, instead). The marker may be drawn using any combination of the null, R.attr.state_pressed, R.attr.state_selected and R.attr.state_focused attributes.

OK, that looks perfect.  Go ahead and give each OverlayItem the Drawable icon you want to use.  Look at the map and… wait a minute.  Nothing shows up.  Why is that?  It works when you haven’t specified the marker…

Well, what this method fails to inform you is that you must define the bounds of a Drawable object before you use it on a map.  The definition of Drawable says

The setBounds(Rect) method must be called to tell the Drawable where it is drawn and how large it should be. All Drawables should respect the requested size, often simply by scaling their imagery. A client can find the preferred size for some Drawables with the getIntrinsicHeight() and getIntrinsicWidth() methods.

So to solve the problem we must do the following:

Drawable icon = getResources().getDrawable(someicon);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
personItem.setMarker(icon);

I wish that an icon defaulted to having its drawable bounds be the rectangle (0, 0, width, height), but you must explicitly define these bounds.

Categories: Android, programming, regular Tags: