Wednesday, December 21, 2011

'Tis the season for sharing (via your Android code)


Come Facebook, come Twitter, come Tumblr and Flickr!

No app is an island, we often want to be able to share the cool things we create with our apps with the world, whether it's the latest social network to on-sell our personal details, or just plain old email to your gran.



.. And here's how.

Lets say we have an image file located here:
String fileLocation = "/mnt/sdcard/SomeMediaFiles/aMediaFile10.jpg"

We can share this easily by passing the reference to our file location into the below method:
 
    public void shareFile(String newFilePath) {
      {Intent share = new Intent(Intent.ACTION_SEND);
      share.setType("image/jpeg"); 
 
//declare the MIME type of the file. .. What you pass into share.setType determines what apps appears in your Sharing list.

      Uri imageUri = Uri.fromFile(new File(newImagePath));
      share.putExtra(Intent.EXTRA_STREAM, imageUri);
   
      startActivity(Intent.createChooser(share, "Share"));
 }
}
And this is basically what is the result (actual apps listed will depend on what is installed on your users device):


... Easy.


Saturday, December 10, 2011

OutOfMemory errors when populating a Gallery


I've just spent the last couple of hours wrestling with OutOfMemory errors when populating a Gallery widget with photos, and as fun as it's been, I think I've sorted it out and thought I'd share my solution with you.

For starters, the reason we're getting OutOfMemory errors in the first place is that the heap size on Android devices is limited to something like 16 MB on a G1 and 24 MB on a Nexus one.

As soon as you start to work with media files you quickly start to learn that you need to work to manage your memory usage with Android as a result of this limitation.

I would also like to thank direct to device debugging, I couldn't have done this without utilizing it, I would go as far as saying that it is essential in situations like this.

Here is the full code of one of my imageAdapters,  I'm using this to provide the images to my Gallery widget like this:

galleryTop.setAdapter(new ImageAdapterTop(this));


The Adapter class :

public class ImageAdapterTop extends BaseAdapter {
   int mGalleryItemBackground;
   private Context mContext;


    File[] allTopSlices = Utils.getAllTopSlices();  
    String[] fullPathAllTopSlices = new String[allTopSlices.length];
    Uri[] uriAllTopSlices = new Uri[fullPathAllTopSlices.length];


   public ImageAdapterTop(Context c) {
       mContext = c;
       TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
       mGalleryItemBackground = a.getResourceId(
               R.styleable.HelloGallery_android_galleryItemBackground, 0);
       a.recycle();
   }


   public int getCount() {
       return uriAllTopSlices.length;
   }


   public Object getItem(int position) {
       return position;
   }


   public long getItemId(int position) {
       return position;
   }


   public View getView(int position, View convertView, ViewGroup parent) {
    int screenHeightPx = Utils.GetScreenHeight(getWindowManager().getDefaultDisplay());
   
    for(int i = 0; i < allTopSlices.length; i++){
    fullPathAllTopSlices[i] = allTopSlices[i].getAbsolutePath();
    }
   
    for(int j=0; j < fullPathAllTopSlices.length; j++){
    uriAllTopSlices[j] = Uri.parse(fullPathAllTopSlices[j]);
    }
   
       ImageView i = new ImageView(mContext);
     
       recycleDrawable(i);


       i.setImageBitmap(Utils.readBitmap(uriAllTopSlices[position].toString()));
     
       i.setLayoutParams(new Gallery.LayoutParams(400, (screenHeightPx-50) /3));
       i.setScaleType(ImageView.ScaleType.FIT_XY);      
       i.setBackgroundResource(mGalleryItemBackground);


       System.gc();
       return i;
     
   }
}



and the referenced recycleDrawable :

private void recycleDrawable(ImageView i) {
BitmapDrawable currentBitmapDrawable = (BitmapDrawable)i.getDrawable();

if(currentBitmapDrawable != null){
currentBitmapDrawable.getBitmap().recycle();        
}
        System.gc();
}



and readBitmap methods:

public static Bitmap readBitmap(String selectedImage) {
Bitmap bm = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;


try {
bm = BitmapFactory.decodeFile(selectedImage, options);
}
catch (OutOfMemoryError e) {
e.printStackTrace();
}

return bm;
}

Probably worth mentioning that the 'options.inSampleSize' reference, which is downsizing the image is pretty much essential when working with large files with the memory constraints associated with current Android devices.

Hope that helps someone out there.


Sunday, November 20, 2011

'An error has occurred' when working with Bitmaps



On a recent project I needed to rotate and slice images taken from the camera, and I found that I was getting regular, but annoying to track down 'an error has occurred' messages.

I couldn't see anything in particular that was wrong with my code, and what made it worse was that I wasn't receiving any errors at all in the emulator, only when I deployed the app to my phone for testing.

It took a few hours to figure out what was going on, so I thought I'd share it here and maybe save somebody else some time.

Like a lot of errors, it seemed painfully obvious after the fact, but the cause was I was basically running out of memory when creating the bitmap objects below. From my experience, you can only create one or two bitmap objects before you will encounter issues like this, because the heap size on Android devices is limited to something like 16 MB on a G1 and 24 MB on a Nexus one.
.. That's not a lot of 6 MB photos.

Luckily, it's super easy to fix.
All you need to do is recycle the bitmaps once you've finished with them, as per below code, and as pointed out by Romain Guy here,

"..The problem is that it can take a couple of GC cycles for a Bitmap to be properly released on Android before Android 3.x. Even if you call recycle() I believe the bitmap counts against your heap usage until at least the next GC. This is one of the very few situations where I would advise you to force a GC by calling System.gc()."

Matrix mat = new Matrix();
mat.postRotate(90);

Bitmap photoRotated = Bitmap.createBitmap(bitmapToSave, 0, 0, bitmapToSave.getWidth(), bitmapToSave.getHeight(), mat, false);

Bitmap photoCut1 = Bitmap.createBitmap(photoRotated, 0, 0, photoRotated.getWidth(), heightOfSlice);

photoRotated.recycle();

//save photoCut1 bitmap here

photoCut1.recycle();
System.gc();


So don't forget to recycle and call System.gc() a couple of times when working with large files like images and hopefully, like me, your errors messages will disappear.

Monday, June 13, 2011

How to get the size and orientation of your device screen



Found this great snippet over at http://www.androidsnippets.com/ that allows you to find the size and orientation of the device screen at runtime, and thought I'd share it:

/* First, get the Display from the WindowManager */
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

/* Now we can retrieve all display-related infos */
int width = display.getWidth();
int height = display.getHeight();
int orientation = display.getOrientation();
// see http://androidsnippets.com/get-size-and-orientation-of-the-screen

Saturday, May 28, 2011

How do you make money with your Android app?

Free?
Obviously not the best way to make money, but a good way to get your name out there, and earn some good karma points while you're at it.

Advantages: maximum exposure to users, everyone who might possible have a use for it considers a free app and will probably install your app even temporarily. You get an opportunity to impress them and build a trusted name with them. The Android Market also lists other apps by the same developer, so viewers of your free apps on the market will be exposed to anything else you publish, driving traffic towards any other apps you might have.

Disadvantages: Without charging for the app itself, you have to use advertising or in-app purchases or other tie-ins in order to make money (see next).


Free with Ads?

Advantages: Ad providers such as admob support house ads, which is advertising for your own products. This opens another opportunity to use your free apps to drive traffic towards any paid apps you might have, or other free offerings too.

Disadvantages:

Advertising sdk's usually add an additional weight to your app size, and there's the obvious loss of screen real-estate also. Additionally, people have to actually use your app in order to see and hopefully click on the ads, so unless your app involves a decent amount of user interaction this might not be for you. To make the most of advertising you want to have an app that keeps users coming back to the screen. Something like a countdown timer for instance might not be so suitable for an advertising-based revenue model as the concept involves minimum user face time, users typically set the timer and turn off the screen.

Advertising platforms:


Free with in-app purchases?
Advantages: You get users in the door and using your app, now you can ply them with the ability to purchase additional related goods in-app. Micro-payment systems mean that your users have the option of adding custom content for mere cents, all within the bounds of your free app, and an app that might not generate much interest even at the lowest price point (usually 99c), is able to still generate income. You get the user mass by distributing your core app for free, and then can utilise novelty-driven or premium-functionality requirements towards generating income. Can also be combined with Advertising, but be mindful of how this can adversely affect user perception of your app.

Disadvantages: more complex to implement that advertising, and obviously requires actual additional content which not all apps are suited to. Micropayment systems not available in all countries.

In-app billing/virtual goods/virtual currencies:

Free cut-down or trial version and paid 'pro' version?
Advantages: Similar to in-app purchases and other 'freemium' techniques, you introduce the users to the concept of your app in the free version, then hope that some of these users are willing to pay for additional content once your free version has earned their respect.

Disadvantages: Unlike in-app purchases and the micro-payment model, the paid 'pro' version is restricted by the minimum price point of the market through which you're distributing your app which in the case of the Android Market is 99c. If you don't have the additional content to warrant meeting that initial price threshold, you're flat out of luck.


Paid?

Advantages: You make profit upfront, regardless of how much someone uses your app, and regardless of even if they keep it installed. Once they've past that initial 15 minutes to refund, you profit.

Disadvantages: Android users are a notoriously stingy bunch, although as more Android users could be considered the mainstream demographic rather than the tech-head early adopters, this may change. Piracy can also be an issue, so make use of the licensing validation library provided by Google. Paid apps also run the risk of being undersold by a competing free product that relies on one of the other methods above to generate income. Also, as strange as it might seem, many users balk when confronted with the option of paying 99c for an app for their $500 device.