# Dealing with Google Play Store reviews

### Or “How to keep your sanity while being an app publisher”

It has been roughly a year since I’ve published my first significant Android app (DiskDigger), and roughly a month since I’ve published my first paid app (DiskDigger Pro). Over the course of this time, I’ve learned some valuable lessons about human nature, specifically about the nature of the humans that leave star-ratings and write reviews about your app. If you’re a fellow app developer, I hope you’ll commiserate with this post. And if you’re a newcomer to the Google Play Store ecosystem (or the Apple App Store, for that matter), take heart.

Of course, this article assumes that your app actually works, and does what it promises. It assumes that negative reviews are not expected for your app, and come as a surprise to you.

### People Will Be People

We’re all familiar with YouTube comments, and the breathtaking stupidity to which the commenters are guaranteed to stoop. Most of this idiocy comes from the fact that the commenters are able to write anonymously (or at least semi-anonymously), which is enough to open the floodgates of ignorance, hatred, bigotry, trolling, and everything else. Much of this mentality translates right over to app reviews.

If you’re an app developer, you will get bad reviews. Get used to it. This is for a very simple reason: there’s a significant imbalance in the activation threshold for writing a good review versus a poor review. In order for someone to give your app a five-star rating, and a good review, they have to be extremely impressed by it. However, in order for someone to give a one-star rating, they only have to find a single wrong thing in the app! Maybe it’s some portion of the interface they find annoying, or some behavior they didn’t expect, etc.

This is fairly similar to reviews of restaurants that we find on Yelp. Most of the positive reviews on Yelp come from people who write reviews as a hobby (who make it a point to write reviews of every place they visit), whereas the negative reviews are from people who happen to catch the wait staff having an off day, and write a review on Yelp when they otherwise wouldn’t.

If you worry about pleasing all of your users, you will burn out. For one thing, if you try implementing everyone’s feature requests, your app will become a disjointed mess, and will likely be used by fewer people than before. Choose very carefully which feature requests to implement, and acknowledge that certain users simply cannot be helped by your app, even if their poor review is constructive.

And another thing: if you try implementing everyone’s feature requests, you will develop feelings of resentment towards your users when they continue to give poor ratings (which they will, for the reasons stated above). You will say, “How dare you rate my app poorly, when I’ve spent so much time implementing features that others have asked for!” These kinds of emotions are highly destructive, and pave the way towards madness.

### Don’t Expect Them to Read Instructions

Asking your users to read any kind of instructions prior to using your app is asking too much. Case in point: My DiskDigger app only works with rooted devices. I state this in the app description several times, and very plainly. I also put the word “root” in the title of the app. And yet, there have still been people who wrote a review to the effect of, “When I launch the app, it says I need a rooted device. Why wasn’t I warned about this?” No, I’m not joking. Read the reviews for yourself, if you like.

If your app requires the user to have any kind of a priori information before using the app, be prepared to receive poor ratings from users who weren’t aware of it.

### Other Oddities

Some people seem to think that giving a one-star rating is a good way of asking for help. I have received numerous one-star ratings where the user says, “Can someone help root my phone?” or, “Can you implement feature X?” I’m not sure how to deal with such “reviews,” except to shrug my shoulders and move on, since the sight of the one star is enough of a turn-off to not want to help this person to begin with. Also, while it’s possible for them to change their rating after they’ve been helped, the time-to-reward ratio is really not worth it. They’re always welcome to contact me directly, anyway.

On the flip side, other people have written five-star reviews in order to come to my defense against the one-star ratings. While I certainly appreciate these kinds of sentiments (since they balance out the trolls somewhat), I would rather get uniformly honest reviews of the software itself, rather than meta-bickering in the comments.

### Responding to Reviews

The Google Play Store allows developers to respond to each review. However, the current mechanism for doing this is deeply flawed, because the responses are posted publicly, underneath each of the reviews!

Furthermore, the responses are limited to 350 characters! This is hardly ever sufficient to thoroughly answer the user’s questions, or guide them towards resolving their issues.

All of this creates a hostile environment, where the developer is encouraged to come down to the same level as the reviewers. That’s not to say that the reviewers aren’t smart, or don’t have legitimate issues or concerns. It simply encourages the developer to become defensive, or even argumentative, towards the users. It’s almost as if Google is saying, “Hey, look what this guy wrote about your app! Are you gonna let him get away with that? Use these 350 characters to stand up for yourself!”

Lastly, the very notion of “responding to reviews” encourages the developer to constantly check the reviews. For a developer who is prone to OCD (like myself), this kind of thing can be very hazardous to one’s mental health! Even now, a part of myself is desperate to log on to my Play Store console, and check for any new activity.

Here are a couple suggestions that would improve the Play Store experience for users, as well as for developers:

Before allowing a user to write a review, ask if the user wants to contact the developer for support! It’s baffling why Google doesn’t display contact and support information for each app much more prominently than it currently does.

If the user selects anything less than five stars, make a text box that slides out and says, “Having issues or any questions regarding this app? Ask the developer for help!”

If that’s too much to ask, then at least allow developers to respond to reviews privately, and directly over email. Responding privately instantly changes the dynamic of the conversation. It also consolidates the number of support venues that the developer has to worry about, since the issue has been moved to email, which should be the primary support venue.

And if that’s too much to ask, then at least notify developers when the user has read the response, as well as when the user updates or amends the rating to which you replied.

### Like Water Off a Duck’s Back

Let’s compare app reviews to restaurant reviews one more time. There are several key differences between the two.

A successful, established restaurant might get, say, five reviews on Yelp per week, at the most. Therefore, the manager of the restaurant might do well to read each review (and will have time to do so), to see things like which menu items are trending and which ones aren’t, how the service staff is performing, and so on.

However, a successful app can get dozens of reviews per day. It’s therefore completely impractical for the developer to pay attention to each one. At this point in the app’s lifecycle, it’s more useful to look at the trends of your star-rating (e.g. on a weekly basis). If the rating takes a dive shortly after you publish an update, it might indicate that the update contains a bug, and merits further investigation. (If the app really does contain a bug, you’ll receive messages from users via email, anyway.)

There’s one more crucial difference between app reviews and restaurant reviews. Restaurant reviews are generally written by intelligent adults with a discerning palate. App reviews, however, can be written by anyone in the world, including 13-year-old trolls, 90-year-old senile grandparents, and everyone in between.

If you haven’t guessed this by now, the proper way to deal with negative reviews is to simply let them roll off you, like water off a duck’s back. Most importantly, don’t let them get to you: don’t let them affect your work or break your spirit.  They will always be there; get over it.

Whether or not you make money from writing software, it should at least make you happy. If you find that it isn’t, then you’re doing it wrong. The fact that a single other person wants to use your software should be reward enough. The fact that your app gets occasional negative reviews simply means that your app has reached that level of popularity, and that’s something to celebrate, not fret over.

# DiskDigger Pro for Android!

I’m pleased to announce the release of DiskDigger Pro for Android! This new version of DiskDigger is capable of recovering (carving) over 20 different types of files from your Android device’s internal memory, or an external memory card. This includes support for .JPG photos, .MP3 and .WAV audio, .MP4 and .3GP video, raw camera formats, Microsoft Office files (.DOC, .XLS, .PPT), and more!

As with the non-Pro version of DiskDigger for Android, this app requires root privileges on the Android device. The non-Pro version of DiskDigger will remain available (for free!) on the Google Play store, and can still be used for recovering .JPG photos.

So what are you waiting for? Go to the Google Play store on your Android device, and install DiskDigger Pro today!

# My BASIC beginnings

Edsger Dijkstra was absolutely right when he said, “Programming in BASIC causes brain damage.”  (Lacking a source for that quote, I found an even better quote that has a source: “It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration.”)

When I reflect on my (not-too-distant) programming infancy, I often think about what I might have done differently, like what technologies I could have learned, which ones I should have avoided, or what algorithms I could have used in old software I had written, and so on.

But there’s one thing that really stands out more than anything else:  starting out with BASIC was the worst thing I could have done.

I’m not sure how useful it is to talk about this now, since BASIC has pretty much gone extinct, and rightly so, but it feels good to get it off my chest anyway.

My parents and I immigrated to the U.S. in 1991, when I was 10 years old, and I had never laid eyes on a personal computer before that time. During my family’s first few months in the States, we acquired a 80286 IBM PC, which was probably donated to us by an acquaintance (since the 80386 architecture was already prevalent at that time, and 80486 was the cutting edge).

I also happened to come across a book called BASIC and the Personal Computer by Dwyer and Critchfield.  I was instantly fascinated by the prospect of programming the computer, and the boundless possibilities that computer software could provide.

However, I made a critical error that would hinder my programming development for at least a year:  I reached the erroneous conclusion that BASIC was the only language there was!

I had no idea that BASIC was an interpreted language, or indeed what difference there is between an interpreted and a compiled language.  I thought that all software (including the games I played, Windows 3.0, Word Perfect, etc.) was written in BASIC!  This unfortunately led me down an ill-fated path of self-study, which took an even stronger effort to undo.

I learned all there was to know about BASIC programming in a few months (starting with GW-BASIC, then moving to QuickBASIC), and then I started to notice certain things about the software I was trying to write.

No matter how I tried, I couldn’t make my programs be as fast as other software I used. I couldn’t understand why this was the case.  Also, the graphics routines in BASIC were virtually nonexistent, so I was baffled how anyone could write games with elaborate graphics, scrolling, and responsive controls.  I was eager to start developing games that would rival my favorite games at the time, like Prince of Persia, Crystal Caves, and Commander Keen.  But the graphics and responsiveness of those games was orders of magnitude beyond what I could achieve with my BASIC programs.

With all this frustration on my mind, I was determined to find the reason why my programs were so limited.  I soon found a solution, but once again it was the wrong one!  I stumbled upon some example BASIC code that used assembly language subroutines (encoded as DATA lines in the BASIC program), as well as INTERRUPT routines that took advantage of the underlying DOS and BIOS services.

This led me down the path of learning Intel 286 assembly language (another few months of studying), and encoding it into my BASIC programs!  This solved the issue of responsiveness, but there was still the issue of graphics, or lack thereof.  Fortunately, I found a book at the local public library about VGA graphics programming. Even more fortunately, the book contained sample source code, using a language they called “C“….

And my eyes were open!

It hit me like a freight train. I was lucky that I didn’t have a seizure right there at the library.  I realized that I had been learning the wrong things all along!  (Of course learning assembly language was sort of right, but my application of it was still misguided.)

Learning C and C++ from that point forward wasn’t particularly difficult, but I still feel like it would have been a lot easier if my mind hadn’t been polluted by the programming style and structure that I learned from BASIC.  It makes me wonder how things might have been different, had I accidentally picked up a book on C++ instead of a book on BASIC during my earliest exploits with computers.

In all fairness, I’m sure I learned some rudimentary programming principles from BASIC, but I’m not sure that this redeems BASIC as a learning tool. There were just too many moments where, while learning C++, I thought, “So that’s the way it really works!”  And I’m sure it’s also my fault for trying to learn everything on my own, instead of seeking guidance from someone else who might have told me, “You’re doing it wrong.”

All of this makes me wonder what programming language would be appropriate for teaching today’s generation of young programmers.  Based on my comically tragic experience with BASIC, my gut instinct is to advise aspiring developers to stay away from interpreted languages (such as Python), or at the very least understand that the interpreted language they’re learning is useless for developing actual software. I don’t think there’s any harm in diving right into a compiled language (such as C++), and learning how it hugs the underlying hardware in a way that no interpreted language ever could.

That being said, I don’t wish any of this to reflect negatively on Dwyer and Critchfield’s BASIC and the Personal Computer.  It’s a solid book, and I still own the original copy.  There’s no denying that it was one of the first books that got me interested in programming, and for that I’m thankful.  However, sometimes I regret that I didn’t find Stroustrup’s The C++ Programming Language at the same garage sale as where I found BASIC and the Personal Computer.  Or, alternatively, perhaps Dwyer and Critchfield could have included the following disclaimer in large bold letters: This is not the way actual software is written!  But perhaps it’s time to let it go. I didn’t turn out so bad, right?

# DiskDigger now available for Android!

I’m happy to announce that DiskDigger is now available for Android devices (phones and tablets running rooted Android 2.2 and above)! You can get the app by searching for it on the Google Play Store from your Android device.  Please note that the app only works on rooted devices.

At the moment, the app is in an early Beta stage, meaning that it’s not meant to be as powerful or complete as the original DiskDigger for Windows, and is still in active development.  Nevertheless, it uses the same powerful carving techniques to recover .JPG and .PNG images (the only file types supported so far; more will follow) from your device’s memory card or internal memory.

So, if you’ve taken a photo with your phone or tablet and then deleted it, or even reformatted your memory card, DiskDigger can recover it!

I’ve written a quick guide that has more information and a brief introduction to using the app!  If you have questions, comments, or suggestions about the app, don’t hesitate to share them!

Update: thanks to Lifehacker for writing a nice article!

I seem to be very minimal in my strategy of organizing my digital photo collection. I have a single folder on my computer called “Pictures,” and subfolders that correspond to every year (2011, 2010, …) since the year I was born. Some of the years contain subfolders that correspond to noteworthy trips that I’ve taken.

This method makes it extremely easy to back up my entire photo collection by dragging the “Pictures” folder to a different drive. It also makes it easy to reference and review the photos in rough chronological order. This is why I’ve never understood the purpose of third-party “photo management” software, since most such software inevitably reorganizes the underlying directories in its own crazy way, or builds a proprietary index of photos that takes the user away from the actual directory structure. If you’re aware of the organization of your photos on your disk, then any additional management software becomes superfluous.

At any rate, there is one slight issue with this style of organizing photos: all of the various sources of photos (different cameras, scanners, cell phones, etc) give different file names to the photos! So, when all the photos are combined into a single directory, they often conflict with each other, or at the very least become a disjointed mess. For example, the file names can be in the form DSC_xxxx, IMG_xxxx, or something similar, which isn’t very meaningful. Photos taken will cell phones are a little better; they’re usually composed of the date and time the photo was taken, but the naming format is still not uniform across all cell phone manufacturers.

Thus, the optimal naming scheme for photos would be based on the date/time, but in a way that is common between all sources of photos. This would organize the photos in natural chronological order. The vast majority of cameras and cell phones encode the date and time into the EXIF block of each photo. If only there was a utility that would read each photo, and rename it based on the date/time stored within it. Well, now there is:

This is a very minimal utility that takes a folder full of photos and renames each one based on its date/time EXIF tag. As long as you set the time on your camera(s) correctly, this will ensure that all your photos will be named in a natural and uniform way.

The tool lets you select the “pattern” of the date and time that you’d like to apply as the file name. The default pattern will give you file names similar to “20111028201345.jpg” (for a photo taken on Oct 28 2011, 20:13:45), which means that you’ll be able to sort the photos chronologically just by sorting them by name!

# The FujiFilm .MPO 3D photo format

A few weeks ago my dad, in his love for electronic gadgetry, purchased a FujiFilm FinePix REAL 3D camera. The concept is pretty simple: it’s basically two cameras in one, with the two sensors spaced as far apart as an average pair of human eyes. The coolest thing about the camera is its LCD display, which achieves autostereoscopy by using a lenticular lens (kind of like those novelty postcards that change from one picture to another when you look at them from different angles), so if it’s held at the right angle and distance from the eyes, the picture on the LCD display actually appears 3-dimensional without special glasses!

Anyway, I immediately started wondering about the file format that the camera uses to record its images (as well as movies, which it also records in 3D). In the case of videos, the camera actually uses the well-known AVI container format, with two synchronized streams of video (one for each eye). In the case of still photos, however, the camera saves files with a .MPO extension, which stands for Multiple Picture Object.

I was expecting a complex new image specification to reverse-engineer, but it turned out to be much simpler than that. A .MPO file is basically two JPG files, one after another, separated only by a few padding zeros (presumably to align the next image on a boundary of 256 bytes?). Technically, if you “open” one of these files in an image editing application, you would actually see the “first” image, because the MPO file looks identical to a regular JPG file at the beginning.

I proceeded to whip up a quick application in C# to view these files (that is, view both of the images in each file). This quick program also has the following features:

• It has a “stereo” mode where it displays both images side by side. Using this feature you can achieve a 3D effect by looking at both images as either a cross-eyed stereogram (cross your eyes until the two images converge, and combine into one) or a relaxed-eye stereogram. You might have to strain your eyes a bit to focus on the combined image, but the effect truly appears 3-dimensional.
• In “single” mode, the program allows you to automatically “cycle” between the two images (a wiggle-gram, if you will), which creates a cheap jittery pseudo-3D effect (see screen shots below).
• Also in “single” mode, the program lets you save each of the frames as an individual JPEG file by right-clicking on the picture.
So, if you want a quick and not-so-dirty way of viewing your MPO files, download the program and let me know what you think! (Or browse the source code on GitHub)
Here’s a screenshot of the program in “stereo” mode:

And a screenshot of the program in “cycle” mode:

If you like, you can download the original .MPO file shown in the screenshots above.

Now for a bit of a more technical discussion…. Clearly it would be a great benefit to add support for the .MPO format to DiskDigger, the best file carving application in town.

However, from the perspective of a file carver, how would one differentiate between a .MPO file and a standard .JPG file, since they both have the same header? As it is now, DiskDigger will be able to recover the first frame of the .MPO file, since it believes that it found a .JPG file.

After the standard JPG header, the MPO file continues with a collection of TIFF/EXIF tags that contain meta-information about the image, but none of these tags seem to give a clue that this is one of two images in a stereoscopic picture (at least not the tags within the first sector’s worth of data in the file, which is what we’re really interested in).

One of the EXIF tags gives the model name of the camera, which identifies it as “FinePix REAL 3D W3.” Perhaps we can use the model name (the fact that it contains “3D”) to assume that this must be a .MPO file, but I’d rather not rely on the model name, for obvious reasons, although the FinePix is currently the only model that actually uses this format (to my knowledge).

The other option would be to change the algorithm for JPG carving, so that every time we find a JPG file, we would seek to the end of the JPG image, and check if there’s another JPG image immediately following this one. But then, what if the second JPG image is actually a separate JPG file, and not part of a MPO collection?

For the time being, DiskDigger will in fact use the model name of the camera to decide if it’s a .MPO file or just a regular .JPG file. The caveats of doing this would be:

• It won’t identify .MPO files created by different manufacturers.
• It might give false positive results for .JPG images shot with the camera in 2D mode.

As always, you can download DiskDigger for all your data recovery needs. And if anyone has any better ideas of how to identify .MPO files solely based on TIFF/EXIF tags, I’d love to hear them!

Update: DiskDigger now fully supports recovering .MPO files, based on deep processing of MP tags encoded in the file!

# Thumbnail cache in Windows 7 / Vista – a rumination

Today I was thinking about the security implications of thumbnail caching systems on most PCs out there today. What I mean by that is this: whenever you use Windows Explorer to browse a directory that contains photos or other images, and you enable the “thumbnail view” feature, you would see a thumbnail of each of the images. By default, Windows caches these thumbnails, so that it doesn’t have to regenerate the thumbnails the next time you browse the same folder.

This has several implications in terms of privacy and security, since it means that a copy of each image is made elsewhere on the computer (albeit lower resolution), basically without the user’s knowledge. This is good news from a forensic examiner’s point of view, since the thumbnail cache can contain thumbnails of images that have long been deleted. However, from the user’s point of view, it can present a privacy/security issue, especially if the images in question are confidential or sensitive.

Windows XP caches thumbnails in the same folder as the original images. It creates a hidden file called “Thumbs.db” and stores all the thumbnails for the current folder in that file. So, even if the original images were deleted from the folder, the Thumbs.db file will still contain thumbnails that can be viewed at a later time.

However, in Windows 7 and Windows Vista, this is no longer the case. The thumbnails are now stored in a single centralized cache under the user’s profile directory: C:\Users\[username]\AppData\Local\Microsoft\Windows\Explorer\thumbcache*.db

The above directory contains multiple thumbnail cache files, each of which corresponds to a certain resolution of thumbnails: thumbcache_32.db, thumbcache_96.db, thumbcache_256.db, and thumbcache_1024.db.

So then, wouldn’t you like to find out what thumbnails your computer has cached in these files? Well, now you can! I’ve whipped up a small utility for the sole purpose of viewing the contents of these thumbnail caches:

This is probably not the first utility that does this, but it’s definitely the simplest. It automatically detects the thumbnail caches present on your computer, and lets you view all the thumbnail images in each cache.

If you want to disable the thumbnail cache in Windows 7 or Vista, you can find instructions here.

# Discovering the 3D Mandelbulb

There is some exciting news this week in the world of fractals. Daniel White, on his website, describes what is apparently a completely new type of fractal, and the closest analog so far to a true 3-dimensional Mandelbrot set!

Although White mentions that this is probably not the “true” 3D Mandelbrot, the new fractal is undoubtedly a sight to behold, especially considering the renderings he showcases on his webpage.

Unable to contain my enthusiasm, I quickly wrote up a small program that uses OpenGL to actually display this shape in 3D, in real time, to get a feel for what this beast looks like from all angles. Don’t get too excited; the program does not render the shape in real time, it just displays the points rendered so far in real time. The actual rendering process can take a minute or so.

The program basically renders the 3D shape by constructing a “point cloud” that approximates the edge of the fractal.

Everything in the program should be relatively self-explanatory, but here’s a brief overview of the features so far:

• The program lets you click-and drag the rendered shape to rotate it in trackball fashion (left mouse button), as well as zooming in and out (right mouse button).
• The program lets you select the “power” of the Mandelbulb formula, as well as the number of iterations to perform.
• The program lets you select the resolution of the point cloud.
• It gives you a “selection cube” with which you can select a subset of the shape to zoom in on (with the “zoom to cube” button).
• It has a number of other minor features like fog and anti-aliasing.
• It uses multiple threads to render the shape, so it will take advantage of multiple cores/processors.

Here are some additional screen shots:

Manipulating the selection cube:

After zooming in on the cube:

Zooming in further:

Looking inside:

Colorized points:

The program was written in C# .NET, using the Open Toolkit Library (OpenTK) which provides an excellent OpenGL wrapper.

Of course, this program is very much in its early stages, so don’t expect it to be perfect. As always, comments and suggestions are welcome!

# The Math Book: Get it Now!

Cliff Pickover, the prolific author of more than forty popular science and mathematics books, has outdone himself with his latest compilation: The Math Book. This is a collection of 250 “milestones” of mathematics throughout history, complete with breathtaking glossy color illustrations for each entry (a first for his books), as well as insightful descriptions that explain the history and the significance of each of these marvels of mathematics.

This book is especially significant in one other way: it contains my artwork! The book’s entry on Knight’s Tours (p. 186) familiarizes the reader with the history of this problem, dating all the way back to Euler in 1759. And, alongside the article, Pickover displays a 30×30 knight’s tour that was solved by my neural network knight’s tour implementation. For the picture in the book, I used a modified version of the program that generated a sufficiently hi-res image. That particular knight’s tour took about 3 days for my computer to generate.

I’m deeply grateful to have one of my creations published in a book by someone as influential as Cliff Pickover. Of course, it’s all of the 250 entries in the book that make it an incredibly fascinating stroll through the history of mathematics. As mentioned elsewhere, this book definitely has bestseller potential, and could easily be one of Pickover’s best works. Buy the book now!

# Beginning Java ME: A Simple Mandelbrot Viewer

Version 3.0 of the Java ME SDK from Sun makes it very easy to start developing Java applications for mobile devices. The SDK comes with a full-fledged (and surprisingly usable) IDE, as well as a suite of emulators and example applications, all of which allows you to pick up and start writing your own apps in no time. I’ve always wanted to develop a few small applications that I could run from my phone, so I thought this might be a good time to start.

My first idea was to develop a very basic Mandelbrot set renderer and zoomer. Sure, it’s not very "useful," but it’s definitely pretty, and you’ll know that you’ll always have one of the defining symbols of modern mathematics right at your fingertips.

And, of course, it’s an awesome way to pick up chicks at the bar or dance club. I mean, come on, flash ‘em the old Mandelbrot set, and they’ll be all over you, am I right? Anyone?

Just as a refresher, the Mandelbrot set is plotted by iterating through the complex quadratic polynomial zn+1 = zn2 + c, where c is each “pixel” within the range of the complex plane we want to plot. If the sequence is bounded for a given c (within a certain number of iterations), the point is considered part of the Mandelbrot set, and the pixel is colored black. If the sequence is unbounded for a given c, the color of the pixel is determined by how “fast” the sequence diverges. The more iterations we take for our calculation, the more “precise” the set’s boundary will be.

Here are some fundamental requirements for the application:

• Render the Mandelbrot set using a predefined color palette
• Allow the user to "move" the drawing by pressing the L, R, U, and D keys
• Allow the user to "zoom" in and out of the Set by pressing, say, the 1 and 3 keys
• Allow the user to decrease/increase iterations by pressing, oh I don’t know, the 7 and 9 keys, respectively

### Creating a New Project

Now that we have some requirements, let’s get down to business. We’ll create a brand new project in the Java ME SDK, and we’ll call it something original, like Mandelbrot:

Using all of the defaults for the purposes of this project is just fine. The SDK should generate a project that targets CLDC 1.1 (Connected Limited Device Configuration) and MIDP 2.0 (Mobile Information Device Profile). It will also automatically create a MIDlet class that represents our new application.

The automatically-generated class descends from the base MIDlet class, and implements the CommandListener interface, which enables our app to "listen" to commands that we can assign to buttons on your phone. The class will be called something like HelloMIDlet, but we can easily rename it to something more pertinent like MandelbrotApp, and erase the "hello world" code so we have a totally clean class.

Theoretically, however, in addition to a class that extends MIDlet, we’ll also need a class that extends the base class Canvas (we’ll call it MandelbrotCanvas), so that we can perform any kind of graphics operations we need. This class will also be responsible for capturing keystrokes from the phone’s keypad (this is different from capturing "commands" which the MIDlet class does).

### The Code

Let’s first go over the MandelbrotApp class. Here are the first few lines of the class, followed by some explanation:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class MandelbrotApp extends MIDlet implements CommandListener { private Display myDisplay; private MandelbrotCanvas myCanvas; private Command exit = new Command ("Exit", Command.EXIT, 1); private Command about = new Command ("About", Command.ITEM, 2);   private int[] colorPalette; private int[] scanlineBuffer; private int screenWidth = 0, screenHeight = 0;   public float rangex1 = -2F, rangex2 = 0.5F, rangey1 = -1.4F, rangey2 = 1.4F; public int numIterations = 24;     public MandelbrotApp () { super ();   myDisplay = Display.getDisplay (this); myCanvas = new MandelbrotCanvas (this); myCanvas.setCommandListener (this); myCanvas.addCommand (exit); myCanvas.addCommand(about);   scanlineBuffer = null; colorPalette = new int[256];   for(int i=0; i<64; i++) colorPalette[i] = (((i * 4) << 8) | ((63 - i) * 4)); for(int i=64; i<128; i++) colorPalette[i] = ((((i - 64) * 4) << 16) | (((127 - i) * 4) << 8)); for(int i=128; i<192; i++) colorPalette[i] = (((255) << 16) | ((i - 128) * 4)); for(int i=192; i<256; i++) colorPalette[i] = ((((255 - i) * 4) << 16) | (255));   }

In the above code, we perform some initialization of things we’ll use later on. First we create an instance of our canvas object (MandelbrotCanvas), and assign two commands ("exit" and "about") to the canvas. This means that when the canvas becomes visible, these two commands will become available from the two top buttons of your handset’s keypad. Also, by setting the canvas’ CommandListener to this, we’re saying that this class will handle the commands issued while the canvas is displayed. The variables rangex1, rangex2, rangey1, and rangey2 represent the initial boundaries for our Mandelbrot calculation. By manipulating these variables we can pan and zoom in and out of the image.

We also define a color palette that we’ll use when rendering the Mandelbrot set. The color palette contains 256 entries, and is simply a gradient of colors from red, to green, to blue, and back to red. Additionally, we define a variable called scanlineBuffer which will actually contain the screen contents before they’re painted onto the screen. We leave this initialized to null, because we’ll dynamically allocate this buffer the first time our paint event is called.

Next, we’ll create a function that actually renders the Mandelbrot set onto our screen buffer. This function uses the standard Mandelbrot algorithm without any fancy attempts at optimization, so it may be slower for some phones than others (on some phones it will be god-awful slow; sorry!).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public void RenderMandelbrot(){ if((myCanvas.getWidth() != screenWidth) || (myCanvas.getHeight() != screenHeight)){ screenWidth = myCanvas.getWidth(); screenHeight = myCanvas.getHeight(); scanlineBuffer = new int[screenWidth * screenHeight]; }   float bmpWidth = (float)screenWidth; float bmpHeight = (float)screenHeight;   float x, y, xsquare, ysquare, dx, dy, bail = 4, j, p; int i, mul, col; int xpos, ypos; float[] q = null;   if(screenWidth > screenHeight) q = new float[screenWidth + 1]; else q = new float[screenHeight + 1];   mul = 255 / numIterations; dx = (rangex2 - rangex1) / bmpWidth; dy = (rangey2 - rangey1) / bmpHeight;   q[0] = rangey2; for(i=1; i < q.length; i++) q[i] = q[i - 1] - dy; xpos = 0; ypos = 0;   for(p = rangex1; p <= rangex2; p += dx){ i = 0; for(j = rangey1; j <= rangey2; j += dy){ x = 0; y = 0; xsquare = 0; ysquare = 0; col = 1; while(true){ if(col > numIterations){ scanlineBuffer[ypos*screenWidth + xpos] = 0; break; } if((xsquare + ysquare) > bail){ scanlineBuffer[ypos*screenWidth + xpos] = colorPalette[(col*mul)%255]; break; } xsquare = x * x; ysquare = y * y; y *= x; y += (y + q[i]); x = xsquare - ysquare + p; col++; } i++; ypos++; if(ypos >= screenHeight) break; } myCanvas.repaint(); myCanvas.serviceRepaints(); xpos++; if(xpos >= screenWidth) break; ypos = 0; } }

Notice in the above function that, inside the outer loop, we force a repaint of the canvas, so that the user gets a sense of the graphic actually being drawn in real time. If we didn’t do this, the app would be totally unresponsive until all of the image is rendered.

When the app is started, the following function is called, where we set the canvas to be the currently-displayed object, and call the Mandelbrot rendering function:

1 2 3 4 public void startApp () throws MIDletStateChangeException { myDisplay.setCurrent (myCanvas); RenderMandelbrot(); }

Another point of interest in this class is the paint handler. This function actually gets called from the Canvas class (see lower), but I put the paint code in this class for convenience.

1 2 3 4 5 6 7 8 9 10 public void paint (Graphics g) {   g.drawRGB(scanlineBuffer, 0, screenWidth, 0, 0, screenWidth, screenHeight, false);   g.setColor(0xFFFFFF); int fontHeight = g.getFont().getHeight(); int strY = 4; g.drawString("(C) Dmitry Brant", 4, strY, 0); strY += fontHeight; g.drawString("Iterations: " + Integer.toString(numIterations), 4, strY, 0); strY += fontHeight; }

In the above function, all we do is draw our screen buffer to the screen, then write some text over the image, which includes a little copyright message and the current number of iterations used in the Mandelbrot calculation. Note that we dynamically get the height of the phone’s font (and adjust accordingly), since the font varies greatly between different phone models.

The other interesting function in this class is the command handler. This function will be called when either the "Exit" or "About" commands are pressed while our app is running. If "Exit" is pressed, we’ll destroy the application. If "About" is pressed, we’ll display a simple Alert message:

1 2 3 4 5 6 7 8 9 10 11 12 public void commandAction (Command cmd, Displayable disp) { if (cmd == exit) { destroyApp (true); } else if(cmd == about){ Alert alert = new Alert ("About..."); alert.setType (AlertType.INFO); alert.setTimeout (Alert.FOREVER); alert.setString ("Copyright 2009 Dmitry Brant.\nhttp://dmitrybrant.com"); myDisplay.setCurrent (alert); } }

Finally, let’s have a quick look at the MandelbrotCanvas class:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 class MandelbrotCanvas extends Canvas { MandelbrotApp myApp;   MandelbrotCanvas (MandelbrotApp mandelTestlet) { myApp = mandelTestlet; }   void init () { }   void destroy () { }   protected void paint (Graphics g) { myApp.paint (g); }   protected void keyPressed (int key) { int action = getGameAction (key);   float xScale = (myApp.rangex2 - myApp.rangex1); float yScale = (myApp.rangey2 - myApp.rangey1);   boolean gotAction = true, gotKey = true; switch (action) { case LEFT: myApp.rangex1 += (xScale / 16.0F); myApp.rangex2 += (xScale / 16.0F); break; case RIGHT: myApp.rangex1 -= (xScale / 16.0F); myApp.rangex2 -= (xScale / 16.0F); break; case UP: myApp.rangey1 -= (yScale / 16.0F); myApp.rangey2 -= (yScale / 16.0F); break; case DOWN: myApp.rangey1 += (yScale / 16.0F); myApp.rangey2 += (yScale / 16.0F); break; case FIRE: default: gotAction = false; }   if(!gotAction){ switch (key){ case KEY_NUM1: myApp.rangex1 -= (xScale / 4.0F); myApp.rangex2 += (xScale / 4.0F); myApp.rangey1 -= (yScale / 4.0F); myApp.rangey2 += (yScale / 4.0F); break; case KEY_NUM3: myApp.rangex1 += (xScale / 4.0F); myApp.rangex2 -= (xScale / 4.0F); myApp.rangey1 += (yScale / 4.0F); myApp.rangey2 -= (yScale / 4.0F); break; case KEY_NUM7: myApp.numIterations-=4; if(myApp.numIterations < 2) myApp.numIterations = 2; break; case KEY_NUM9: myApp.numIterations+=4; break; default: gotKey = false; } }   if(gotAction || gotKey) myApp.RenderMandelbrot(); }

The only relevant functions in the above class are the paint function, which is called automatically whenever the screen needs repainting, and the keyPressed function, which gets called when the user presses any of the keys on the keypad.

Notice how pressing the Up/Down/Left/Right buttons causes the x- and y-ranges to be panned to simulate the effect of scrolling, and the "1" and "3" keys have the effect of zooming. Keys "7" and "9" are also programmed to decrease and increase the number of iterations by 4. After any key is pressed, the graphic is redrawn due to RenderMandelbrot() being called again.

### Testing the App

That’s about all there is to it. The next step is to test how the app works. If we run the app from the IDE of the Java ME SDK, it automatically launches a default emulator and loads the app onto it:

Seems to work fine on the emulator! Now how about getting it onto an actual phone? Building the app produces two files: a .JAR file, which is the actual app, and a .JAD file, which is a text file that contains certain descriptions about the app (such as the author, copyright, and URL). But what do we do with these files?

Loading a Java app onto a phone can be done in a few different ways:

• Load from a memory card
• Hack right into the phone

I’m fortunate enough to have a Motorola RAZR V3xx, which has a microSD slot. I was amazed how easy it was to get it up and running on this phone. Here’s all that’s required to install a Java app onto this phone (and probably similar Motorola models that accept a microSD card):

• With the microSD card in the phone, connect a USB cable from your computer to the phone (it will map as a disk drive)
• Copy your app’s .JAR and .JAD files to the /mobile/kjava directory
• Disconnect the USB cable from the phone
• On the phone, go to Menu -> My Stuff -> Games & Apps -> [Install New], and select your app from the list
• That’s it! The app will now be installed on the phone!

### Installing over the Web

If you don’t have a phone that lets you install apps from a microSD card, you can download the app from the Web using your phone. This is, of course, only if your phone has an active account and supports Web access. You’ll also probably be paying a few cents for the data transfer, depending on the wireless plan you purchased from your carrier. Oh, and of course, your phone must support CLDC 1.1 (1.0 doesn’t have floating-point support), and MIDP 2.0, which most modern phones do.

Now then, if you want to load an app that you wrote onto your phone over the Web, you need to have access to a web server where you can place your app to be downloaded. For my example, I’ll use my web server, dmitrybrant.com, and I’ll put the app in a subdirectory, like so:

I put both the .JAR and .JAD files in the "jme" subdirectory. To download the app, you only need to link to the .JAD file. But wait! There’s one more crucial step to take before our app can be downloaded from a phone. We need to edit the .htaccess file in this directory, and add the proper MIME types for our files:

AddType text/vnd.sun.j2me.app-descriptor jad AddType application/java-archive jar

Once this is done, we can try downloading the app to our phone. After a minute of fat-fingering the URL, agreeing to install an unsigned app, and waiting for it to finish installing… lo and behold:

For the record, the above screen took about 20 seconds to draw on my V3xx. Your phone may be significantly slower (or faster, but probably not). So be prepared to wait a bit for the drawing to complete. Or go ahead and optimize the code to use integer-only math! (Let me know if you do!)

Browse the source code for this project on GitHub.