My favorite bug #53 - Crop Circles

by Peter_vdL Motorola 09-11-2011 04:11 PM - edited 09-11-2011 08:06 PM

They say you shouldn't play favorites, but I just can't help having a collection of favorite bugs.  I was reminded of this by an excellent posting in the community boards over the weekend.     DongHoon, an Android app programmer, was working on a new feature.  He needed to crop a picture.


Through a google search, he found that the Camera app seems to have this ability.  At least, you can find books and web pages advising:

Use the Camera app to crop an image - send it an Intent with an action string "" and it will do all the work for you.

crop.jpg  A Crop Circle, or a circle, cropped?


Here's the Code

DongHoon tried that. Here's the code he used.

Intent intent = new Intent("");  // ACTION
intent.setDataAndType(imgUri, "image/*");                      // DATA to do it to
intent.putExtra("outputX", cropWidth());   // extra details
intent.putExtra("outputY", cropHeight());
intent.putExtra("aspectX", cropWidth());
intent.putExtra("aspectY", cropHeight());
intent.putExtra("scale", true);
tmpCropUri = createTargetFile();
intent.putExtra(MediaStore.EXTRA_OUTPUT, tmpCropUri); startExternActivityForResult(intent, REQUEST_CODE_CROP, R.string.app_crop);

The Building Block for an App is an Activity

As a reminder, for people who are new to android, the "chunks" of code that make up an app are called "Activities".  Each Activity does a single focused thing, like display and process exactly one GUI screen, or crop exactly one image.  


As well as doing the work within your app, you can "publish" your Activity to the android framework (through your manifest file).  You specify what the Activity can do, and the data that it can do it on.  Other apps can issue orders, saying "I want this thing done to this data", and the framework will look up its table of Activities to find a match. (That's called an implicit intent.  Alternatively, you can specify the class that you want to handle your order - an explicit intent.  You tend to use explicit intents to reach Activities you wrote, and implicit intents when you want something processed by the framework).


The order is issued by constructing an android.content.Intent object. The Android developer docs expresses this rather portentously as "An Intent provides a facility for performing late runtime binding between the code in different applications." (More accurately, the binding is between data in one place, and code in another place, not necessarily different apps).


As you can see in the code above, the two most important fields in the Intent are the action you want done, and the data you want it done to.  


  • There are about 100 string constants for actions.  These are things like ACTION_MAIN and ACTION_EDIT (the two most frequently used).  You can see the whole set in the docs for android.content.Intent.
  • The data is usually in the form of the ever-popular Android URI - a magic string for getting hold of stuff.  A URI might be the path to a file, a URL, the literal data, or a call into a database or app to provide the data.  Example URIs are:

    content://contacts/people/21        // the person with id 21 in the contacts database

    tel:2125551212                            // the telephone number 212 555-1212

    content://media/internal/images   // the list of all images on internal storage (not sdcard).


You may also provide some extra fields in the Intent, giving more details about what you want, where you want the results, etc.

Only One Problem

All very straightforward and workmanlike.  Only one problem - the code didn't work. There was some good news and some bad news. The good news was that the bad news wasn't very bad :smileysurprised:. The bad news was that DongHoon got a mysterious error in the adb log:

15609-0907:21:43.569E5213CropImagegot IOException /data/data/ (No such file or directory)

DongHoon and his coworkers were frankly puzzled.  He wasn't doing anything remotely connected to wallpaper (or was he?), and furthermore the code worked fine on some models of phone, but not on others.  That's when he posted to the MOTODEV Discussion Boards!


The Analysis

Here's what went wrong.   The Intent class is very open-minded about the action strings it will accept.  In particular, if you can see the name of any Action string anywhere in the Android source, you can create your own Intent to fire at the Activity - even if that Activity was never intended to be part of the public API.


That's what the code does here, using a literal string

Intent intent = new Intent("");  // ACTION

The clue that something is wrong here, is the literal string.  Names in the Android public namespace start with "android".  They don't start with the name "".  The use of a name starting with "com." is the tip-off that we are using something outside the public Android API.  And that is a sure recipe for eventual disaster.  Even if it works now, there is no guarantee that it will work in the next release. (That's why DongHoon noticed that it worked on some models of phone with older API levels, but not on the newer ATRIX running GingerBread).


The details of how the implementation changed hardly matter, but I'll give them here for completeness.  There was an Activity called CropImage in the app.  In the Froyo release (Android 2.2, API level 8), the CropImage activity was specialized to work on wallpapers, not any old image.  That accounts for the strange message about a wallpaper temp file missing.  A little later, CropImage was moved from the camera app to the gallery app.  You'd get a different error on a handset with that change.


The Lesson

You can crop an Image by turning it into an, and crunching on that.  There's a good description here.


As Android framework engineer Dianne Hackborn pointed out "If there is no constant in the SDK for a string like "", then this is not a part of the public API, and you should not put it in your apps as if it were.


Breaking this rule will lead only to sorrow, and 1-star user ratings in the Android market.




Peter van der Linden

Android Technology Evangelist

Post a Comment
Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
reCAPTCHA challenge image
Type the characters you see in the picture above.Type the words you hear.
About MOTODEV Blog
The MOTODEV blog keeps you updated on mobile app development news from MOTODEV and the Android developer community.

Subscribe to our RSS feed Subscribe via RSS

Follow Us:
Fan MOTODEV on Facebook Join the MOTODEV LinkedIn Group MOTODEV on YouTube

motodev profile

motodev @david_latham what specifically are you trying to download? 7 days ago · reply · retweet · favorite

motodev profile

motodev RT @StackMob: Join StackMob and Salesforce at the MOTODEV Google + Hangout today at noon. Register here 7 days ago · reply · retweet · favorite

motodev profile

motodev G+ Hangout: Migrating ur legacy systems to the cloud & mobile is at @ 12pm PDT today. Join us and bring your questions. #l2cloud 7 days ago · reply · retweet · favorite

Our Blog & Comment Policy
Opinions expressed here and in any corresponding comments are the personal opinions of the original authors, not of Motorola. The content is provided for informational purposes only and is not meant to be an endorsement or representation by Motorola or any other party.

Remember, when you comment, please stay on topic and avoid spam, profanity, and anything else that violates our user guidelines. All comments require approval by Motorola and can be rejected for any reason.

For customer support issues with your Motorola phone go to the Motorola customer support website.