Archiv for ‘MediaDroid’


published: February 26th, 2010

Polish and Release Early Release Often

CAUTION: Meandering editorial follows…

I’ve been trying for a week to write a post about the recent demo/Android talk I did (along with Mike Koss and Alberto Fonseca) with the Google Technical Users Group in Seattle (huge thanks to Mike Koss for setting this up!) earlier this month.  There’s a comment I made in this talk that has been hounding me ever since.  Since then I’ve wanted to expand on it and make it clear what my position is.  At about 1:08 into the video, I made it sound like getting something out the door is more important than making sure it’s polished.  I strongly believe in the release early and release often mentality.  I have been on far to many projects that have belabored releasing because of some assumed polish that needed to be done.  Whether it was that one more magical feature, or that one more must fix bug, there was always something holding back the release date.  I certainly respect those companies that have the resources and skills to pull the “make sure it’s polished” standard…one of these companies is of course Apple.  They have an awesome sense for design and what customers want, and they have the resources to develop in house and behind closed doors….I don’t…I don’t even have the resources to hire a decent graphic designer or QA person, and if I spent all my time worrying if a certain icon looked pleasing or if that one bug was a show stopper, I’d never, ever get anything out the door.  So as a single developer trying to make the simplest product, I gotta just put it out there and see how it does.  Maybe it’ll suck?  Or, maybe for whatever reason it won’t gain traction.  But wallowing in “does it have enough polish” land doesn’t answer anything, so I choose to get it out the door, and the Android platform allows me to quickly do this.

Maybe I’ll stumble upon that one app that is an overnight success and is so nicely polished that everyone wants to pay me 99 cents for it, but I’m not betting on it…and that statement doesn’t make me feel any less talented then the person who does make that overnight success app.  That person was just luckier to came across (for whatever reason) that one idea that they stuck with and where able to pull off!  And I hugely congratulate them on their success!

Hope you enjoy the little talk we had at Startpad….here’s the link if the embed doesn’t work

published: February 25th, 2010

MediaDroid YouTube demo video

Well here’s my very first YouTube video…it’s a demo of MediaDroid. Certainly not perfect, but I think it gets the job done and I’m stick’n with it!

Here’s the link in case the embed doesn’t work

published: February 16th, 2010

MediaDroid 1.3.4 on Android Market

Just released a brand new version of MediaDroid.  Thanks to some reverse engineering of the Android Image Gallery, I’ve been able to greatly increase the performance and stability of the app.  Hopefully I’ll be able to get into the specific changes (for all the Android developers out there) in a later post when I get a little more time.  Also, improved the button icons thanks to my son’s (OK…they where mine first) Crayons (TM).

Already have plans for some more features (filtering the image buckets shown in the Image Gallery) and with this re-write out of the way I think I’ll be able to get the next version out sooner.

Cheers, and thanks for all the support from the current users of MediaDroid, it’s great to hear all your feedback…good and bad!!

published: February 5th, 2010

Overlay a bitmap on another

As I’ve been alluding to in my Twitter feed, I’m on the cusp of deploying a new version of MediaDroid, huge re-write to image gallery, but hopefully I’ll get a post about that out later.  Anyway, the previous way I was marking the images to be included in the albums was to have a combo view with a check box and a thumbnail of the image.  Although this works, it takes up a lot of screen real estate and isn’t terribly efficient.  The next version of the image gallery I wrote extended the Android ImageView class and although this solution isn’t much more efficient (the solution for that is in my newest version), it certainly increases the number of images viewed at a time.

(more…)

published: November 3rd, 2009

New Version of MediaDroid and Message Drop up on Android Market

I posted a new version of both MediaDroid and Message Drop to the Android Market this morning.

The new version of MediaDroid fixes the reboot bug.  It’s not as elegant as a fix as I’d like but it does work.  FYI to any developers, the SD Card isn’t necessarily mounted when the ACTION_BOOT_COMPLETED event is broadcast.  I used a CountDownTimer to wait 30 seconds after my BroadcastReceiver is called, then I’m able to access the files on the SD Card.

I added a couple features to Message Drop and fixed a few bugs.  The new features are spelled out on the Message Drop page  and include a new Setting for assigning the “Blank Tag” and a mode helping you build bulleted item lists.

My plan is to switch back to MediaDroid and perform some much needed polishing, and I ran into a Force Close that I haven’t been able to track down yet.  Always more to do!

published: October 4th, 2009

MediaDroid “mostly” working with Android 1.6

* UPDATE - Full and Trial version of MediaDroid now have fixes for Android 1.6 layout issues I was having.  Check them out in the Android Market and let me know what you think!

Just got updated to 1.6 from T-Mobile. I love the new searching capabilities and the Power Control Home screen Widget.

homescreen_widgets.png

But it looks like I have some work to be done for one of the layouts for MediaDroid.  Not sure what’s wrong yet, but it looks like the layout for the Manage Album screen isn’t configured correctly and the buttons have been force out of the screen.  Hopefully the fix isn’t to hard and I’ll get a new version of MediaDroid out soon!

mange_album_16oops.png

published: October 1st, 2009

MediaDroid now cycles images from the selected album

Using a Service and ACTION_SCREEN_OFF  I’ve successfully added a new feature to MediaDroid which cycles the images in your photo album.  Every time the screen is put to sleep, the next photo in the album is placed in the photo widget on the Home screen of your Android device.  Here’s a code snippet…

public class ChangeImageService extends Service
{
	private static final boolean LOGD = Constants.LOGD;

	private BroadcastReceiver _receiver;
	private static final String LOG_TAG = "ChangeImageService";

        @Override
	public void onCreate()
        {
		super.onCreate();

	    _receiver = new BroadcastReceiver()
	    {
	        public void onReceive(Context context, Intent intent)
	        {
	        	loadNextImage();
	        }
	    };

	    IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
	    registerReceiver(_receiver, filter);
	}

....
}

A couple of gotchas you have to watch out for… The ACTION_SCREEN_OFF Intent does not get picked up from the AndroidManifest.xml file so you have to assign it in code as shown above.  Also, I put the assignment in the onCreate method because the Service can get killed off.  If the Service is killed off, Android will schedule it to restart, but in order to catch the Screen Off broadcasts the Service must sign up again in the onCreate method…at least, this is what worked for me and all seems to be going well.  As always, the docs are a little sparse and I had to go through some trial and error to figure it out, but I’m pretty happy with the results.

The new feature (along with a bug fix) is available in both the Trial and Full version of MediaDroid in the Android market…check em out and let me know what you think!

published: September 22nd, 2009

Full version of MediaDroid on the Android Market

Just wanted to post a quick note that I’ve released a full version of MediaDroid to the Android Market.  I’ve got lots more feature coming up and there’s a couple of bugs to iron out, but I think it’s feature complete enough for a first version.

Would love to get your feedback, good or bad!  More information with screen shots at http://www.mgmblog.com/mediadroid.

published: September 21st, 2009

My implementation of ImageCache

Been awhile since I’ve posted any code and thought it was about time.  The image gallery has definitely been one of the most challenging parts of the MediaDroid application so far.  I even seem to have uncovered a bug in one of the earlier versions.  But thanks to some advice from the Google I/O talk by Romain Guy I settled on an implementation using SoftReferences and the AsyncTask class.  If you have more experience with threading then I do, I’d be interested in your opinion!

The task of the ImageCache is to load Bitmaps as they are requested.  From the outside the Bitmaps are requested using the get method which takes the Uri of the Bitmap to load, and the ImageView which will eventually be updated with the Bitmap. A single thread waits for the requests or jobs as I call them to be added to a job list.

The ImageCache follows the Singleton pattern and is initiated using the follow call,

ImageCache.getInstance().executeLoadImagesTask( context );

This call initiates a thread which waits for jobs to be posted on the job list.

A Bitmap is retrieved from the ImageCache with the following call

Bitmap bm = ImageCache.getInstance().get( _thumbUri.toString(), _imgVw );

If the image isn’t initially in the cache, a default Bitmap is returned, then a job is added to the job list.  This job contains the Uri to the Bitmap and and the ImageView to update when the Bitmap is done loading.

I also extended ImageView to contain a Uri for the image it displays. When the image is loaded into the UriImageView it is checked against the one that is currently assigned. Otherwise, the ImageView will cycle through all the images that are loaded, kind of a cool effect, but not the best result for the UI.

As always, sorry for the funky formatting, someday I’ll find and install a proper CSS for computer code.  Be weary of cut-n-paste, I’m not sure if I caught all the formatting issues.


public class ImageCache
{
	private static final String LOG_TAG = "ImageCache";
	private static final boolean LOGD = true;

	private static ImageCache _instance = null;
	private final HashMap<String, SoftReference<Bitmap>> _cache;

	private Context _ctx;
	private static Bitmap defaultBitmap;
	private Object _lock;
	private ArrayList<ImageJob> jobs =  new ArrayList<ImageJob>();

	public static ImageCache getInstance()
	{
		if ( _instance == null )
		{
			_instance = new ImageCache();
		}

		return _instance;
	}

	private ImageCache()
	{
		_lock = new Object();
		_cache = new HashMap&ltString, SoftReference<Bitmap>>();
	}

	public void executeLoadImagesTask( Context ctx )
	{
		_ctx = ctx;
        	defaultBitmap = BitmapFactory.decodeResource( _ctx.getResources(), R.drawable.camera_icon );
		new LoadImagesTask().execute(null);
	}

	public void put( String key, Bitmap bitmap )
	{
		_cache.put( key, new SoftReference<Bitmap>(bitmap) );
	}

	public void clear()
	{
		_cache.clear();
	}

	public Bitmap get( String imguri, UriImageView viewToUpdate )
	{

		Bitmap bm = null;
		SoftReference>Bitmap< reference = _cache.get(imguri);

		if ( reference != null )
		{
			bm = reference.get();
		}

		if ( bm == null )
		{
			bm = defaultBitmap;
			postJob( new ImageJob( imguri, viewToUpdate ) );
		}

		return bm;
	}

	/**
	 * Called after the bitmap has been retrieved bitmap from the
	 * sdcard
	 *
	 * @param jobs
	 */
	public void addImage( ImageJob... jobs )
	{
		ImageJob job = jobs[0];
		final UriImageView view = job.getViewToUpdate();
		final Bitmap bm = job.getBitmap();
		final Uri imguri = job.getImageUri();

		put( imguri.toString(), bm );

		view.post( new Runnable() {

			public void run()
			{
				view.setImage(imguri, bm);
			}

		});
	}

	private void dbg( String msg )
	{
		if (LOGD) Log.d( LOG_TAG, msg );
	}

	private void postJob( final ImageJob job )
	{
    		dbg( "Thread: " + Thread.currentThread().getName() + " posting job " + job.getImageUri() );
    		synchronized(_lock)
    		{
    			jobs.add(job);
    			_lock.notify();
    		}
	}

	private class ImageJob
	{
		private Uri _imageUri;
		private UriImageView _viewToUpdate;
		private Bitmap _bm;

		public ImageJob( Uri imageUri, UriImageView viewToUpdate )
		{
			_imageUri = imageUri;
			_viewToUpdate = viewToUpdate;
		}

		public ImageJob( String imageUri, UriImageView viewToUpdate )
		{
			this( Uri.parse( imageUri ), viewToUpdate );
		}

		public Uri getImageUri()
		{
			return _imageUri;
		}

		public UriImageView getViewToUpdate()
		{
			return _viewToUpdate;
		}

		public void setBitmap( Bitmap bm )
		{
			_bm = bm;
		}

		public Bitmap getBitmap()
		{
			return _bm;
		}
	}

	private class LoadImagesTask extends AsyncTask<Object, ImageJob, Integer>
	{
		@Override
		protected Integer doInBackground(Object... params)
		{
			Thread.currentThread().setName( "LoadImagesTask" );

			while( true )
			{
				int jobsize = 0;

				synchronized( _lock )
				{
					jobsize = jobs.size();
				}

				if ( jobsize > 0 )
				{
					synchronized( _lock )
					{
						ImageJob job = jobs.remove(0);
						dbg( "performing image job " + job.getImageUri() );

						try
						{
							job.setBitmap( ImgUtils.buildBitmap(_ctx, job.getImageUri(), 4) );
							onProgressUpdate( job );
						}
						catch (IOException e)
						{
							e.printStackTrace();
						}
					}
				}
				else
				{
					try
					{
						synchronized( _lock )
						{
							dbg( "Thread: " + Thread.currentThread().getName() + " waiting for jobs" );
							_lock.wait();
							dbg( "Thread: " + Thread.currentThread().getName() + " got a job" );
						}
					}
					catch (InterruptedException e)
					{
						e.printStackTrace();
					}
				}
			}
		}

	    @Override
	    public void onProgressUpdate(ImageJob... job)
	    {
	        addImage( job );
	    }

	    @Override
	    public void onPostExecute(Integer imagesLoaded)
	    {
	    	dbg( "loaded " + imagesLoaded + " images" );
	    }

	}
}

Again, I’d love to hear any comments you may have about the implementation, one thing about bootstrapping and doing all the coding yourself is it’s much harder to find code reviewers!

published: August 21st, 2009

MediaDroid let loose

After many long months of thinking, planning, and thinking some more.  The flagship application of my grand scheme is out in the wild.  MediaDroid is an Android Application Widget which allows the user to create albums and add photos to those albums.  The selected photo is displayed on the home screen and can be changed at anytime by the user by selecting a different photo.

The current version of MediaDroid in the Android Market is just a trial version, and it certainly has some performance short comings.  But I believe in release early and release often, so time permitting I will be getting updates out soon, and when I feel it’s up to snuff I’ll release a full version.  The trial version limits the user to 2 albums with 5 photos in each.  The full version won’t have these limitations and other features as well.

A full writeup coming soon, along with screen shots, for right now, it’s way to late and I must get to bed.