25 January 2010

Attaching Android platform source to Eclipse

One of the major drawbacks of Android development is in the missing sources of the framework. At least in my opinion. Attaching the sources seemed like a logical step to me. Easy enough I found Google's step-by-step instructions on how to download the source files from the repo (http://source.android.com/download). This took a huge amount of time and produced some 8 GBs of data on disk. Pretty huge for just some source, huh?
So I kept on searching. Luckily, I found this post, providing zipped pure sources for SDK 1.5, 1.6, and 2.1. Zip files are just around 22 MB. You just create a folder named 'sources' for the related platform and extract the files/folders from the zip into that folder. Once you refresh your projects in Eclipse your sources are there. Awesome! 

15 January 2010

Adding Multitouch support

Since Android now supports multitouch, I wanted to find a code snippet illustrating its usage. Sounds like a piece of cake but actually isn't.
Once you get the job done, implementing multitouch on Android is really simple. See the snippet which will write out the x- and y-coordinates for each finger on the screen (this made me realize the Motorola Milestone can only identify 2 fingers at once) to LogCat:

@Override 
public boolean onTouch(View v, MotionEvent event) { 
    for (int i=0; i<event.getPointerCount(); i++) {
    (...)
        Log.d("Pointer", "Pointer "+(i+1)+": x="+event.getX(i)+", y="+event.getY(i));
    } 
    (...)
} 
I found a topic about this on the Android Google group: http://groups.google.com/group/android-developers/browse_thread/thread/232ed8c380da7b64.
The provided (and simplified) snippet is based on this example app.

08 January 2010

Need to format the source code

Alright, this just looks dumb. I'll look for a way to better integrate (i.e. format) the code snippets which seem plain unusable right now.

Taking photos from the handset's camera via Intents

Taking a photo via the camera is actually a really easy task in Android if using Intents. I provide a little code snippet which illustrates its usage.

Fire up an intent to start the 'photo-taking' activity:
mTakePhoto = (ImageButton)findViewById(R.id.takePhoto);
mTakePhoto.setOnClickListener(new View.OnClickListener()
{
 @Override
 public void onClick(View v)
 {
  // fire off an intent for the camera
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  startActivityForResult(intent, REQUEST_CAMERA);
 }
});
This will launch the 'photo-taking' activity which lets you take a picture. To get (and handle) the result you will need a listener to respond when the image capture is finished:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{  
super.onActivityResult(requestCode, resultCode, data);

 // if Activity was canceled, display a Toast message for 1 second
if (resultCode == RESULT_CANCELED) {
  Toast toast = Toast.makeText(this,"Activity cancelled", 1000);
  toast.show();
  return;
 }

 // lets check if we are really dealing with a picture
if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK)
 {
  String timestamp = Long.toString(System.currentTimeMillis());
  // get the picture
mPicture = (Bitmap) data.getExtras().get("data");

  // save image to gallery
  MediaStore.Images.Media.insertImage(getContentResolver(), mPicture, timestamp, timestamp);

  Uri uri=MediaStore.Images.Thumbnails.getContentUri("external");

  Cursor cursor=MediaStore.Images.Thumbnails.queryMiniThumbnails
  (getContentResolver(), uri, MediaStore.Images.Thumbnails.MICRO_KIND, null);

  Long _imageId = null;
  cursor.moveToFirst();
  while(true){
   for(int i=0;i<cursor.getColumnCount();i++){
    if(cursor.getColumnName(i).equals("image_id")) {
     _imageId = Long.parseLong(cursor.getString(i));
    }
   }
   if(cursor.isLast()){
    break;
   } else {
    cursor.moveToNext();
   }
  }
  // Get Bitmap and scale to default icon size
  Bitmap tmp = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(), _imageId, MediaStore.Images.Thumbnails.MICRO_KIND, null);
  tmp = Bitmap.createScaledBitmap(tmp, 48, 48, true);
  // Update ImageButton icon
  mTakePhoto.setImageBitmap(tmp);

  // save image to SD card
  try {
   File directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/SOME_DIR/");
   if(!directory.exists()) directory.mkdir();
   FileOutputStream fos = new FileOutputStream(directory+"/"+timestamp+".jpg");
   Environment.getExternalStorageDirectory().mkdir();
   mPicture.compress(Bitmap.CompressFormat.JPEG, 90, fos);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}
The listener responds when an photo was taken and lets you access the data. What I am doing with the data is this:
  • add it to the device's media gallery
  • create a bitmap thumbnail to update to button's icon with the just taken picture
  • additionally store the image on the sd-card on an arbitrary location
Obviously, this is quite a lot of code. If you just want to add the picture to the media gallery, code reduces to just 2 lines (after the checks) for the listener:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
 super.onActivityResult(requestCode, resultCode, data);

 // if Activity was canceled, display a Toast message for 1 second if (resultCode == RESULT_CANCELED) {
  Toast toast = Toast.makeText(this,"Activity cancelled", 1000);
  toast.show();
  return;
 }

 // lets check if we are really dealing with a picture if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK)
 {
  // get the picture
  mPicture = (Bitmap) data.getExtras().get("data");

  // save image to gallery
  MediaStore.Images.Media.insertImage(getContentResolver(), mPicture, timestamp, timestamp);
 }
}

Coming clean

So this is it. I decided to post my experiences with Android. Actually, this is my 1st blog ever. Well, okay, I created a couple but never for myself. 'So why do you start this just on Android?' one might ask. Pretty simple: it documents the progress a make. Maybe it's (weird) sort of a diary 2.0. And maybe it's going to help some others, preventing them from running into the exact same time consuming pitfalls I ran into. 


Let's get my hands dirty. What I am going to need (and will be using):
Installation and configuration worked out pretty straightforward.
But when the ADT plugin was updated from 0.9.4 to 0.9.5 Eclipse refused to start at all. I created a thread over at anddev.org to get some help. Since I didn't find any I had to reinstall Eclipse and Android SDK. This turned out to be a much quicker thing than messing around with the old one, combing through various configuration files on different locations. 
Having 'fixed' the issue I just created a backup copy of my entire Eclipse folder, granting a working version at all times.


The next post will provide some first code snippets.