- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Email to a Friend
- Printer Friendly Page
- Report Inappropriate Content
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 "com.android.camera.action.CROP" and it will do all the work for you.
A Crop Circle, or a circle, cropped?
Here's the Code
DongHoon tried that. Here's the code he used.
Intent intent = new Intent("com.android.camera.action.CROP"); // 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);
intent.putExtra("noFaceDetection",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 . The bad news was that DongHoon got a mysterious error in the adb log:
15609-0907:21:43.569E5213CropImagegot
IOException java.io.FileNotFoundException:
/data/data/com.motorola.gallery/files/temp-wallpap
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("com.android.camera.action.CROP"); // 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 "com.android". 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 com.android.camera 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 android.graphics.Bitmap, 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 "com.android.camera.action.CROP", 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.
Cheers,
Peter van der Linden
Android Technology Evangelist