Saturday, September 22, 2012

Making Flickr awesome again (or at least the Android part)

A while back, around the time Marissa Mayer took over Yahoo, a site popped up with the message:

"Dear Marissa Mayer,
 PLEASE MAKE
 flickr AWESOME 
AGAIN".

I've got into photography lately, and awesome or not, Flickr still seems to be the go to place for hobbyist photographers.  Unfortunately the official Android app for Flickr was disappointing.  Sure, it's fairly functional, but it's execution of a lot of this felt dated, the interface is slow and just not what I look for in an Android app.

I had been working a on Twitter app for about 3 months for much the same reasons as above, and it seems I wasn't the only one to feel this way as recently the surge of new holo themed Twitter apps has been massive.  Some of these already far surpassed my vision for my app, and so rather than trying to play catch up, I gave up.  Shortly after, I took some of the work that had gone into my Twitter app and morphed it into Glimmr.

I'm really thrilled to say so far the reaction has been pretty great.  Thanks for reading, enjoy the app!

Sunday, April 1, 2012

Cleaning your Java imports in Vim

The JavaImp plugin for Vim can add and organise imports in Java files at the press of a button.  One thing that it's missing however, is a way to find and remove redundant imports.

Here's one solution I've come up with and would like to share; there may be neater ways but as a first pass I've found this works quite well.

First install the 'checkstyle' package:

sudo apt-get install checkstyle

Secondly, download the following script:
http://www.vim.org/scripts/script.php?script_id=4018
and save it under ~/bin/clean_imports.sh

Finally, add the following to your .vimrc:

" F7 to call clean redundant Java imports and sort them
function JavaImpClean()
    %!~/bin/clean_imports.sh %
    :JavaImpSort
endfunction
:command JavaImpClean exec JavaImpClean()
:nnoremap <F7> :JavaImpClean<CR>


Just hit F7 when editing any Java and have your imports cleaned and sorted. I'm sure with some more time this could be adapted to use a variety of code checkers to work with different languages.

Now all I need is a way to pull in all missing imports without having to go through each one...

Enjoy!

Tuesday, January 24, 2012

A Guide to updating your app for Ice Cream Sandwich

I recently managed to procure myself a Galaxy Nexus, and so started looking into how much it takes to apply 4.0 stylings to my app.

Surprisingly this can be easier than you think!  Read on to find out how.

BeforeAfter


The easy way:
If you're lucky, it can be as easy as changing one line, and rebuilding with the latest sdk.
Provided you don't have too much custom styling applied, try adding the following to your AndroidManifest.xml:

<uses-sdk
    android:targetsdkversion="15">
</uses-sdk>

If you leave the minSdkVersion as is, it should still work as always on pre ICS devices, but automatically apply 'Holo' theme on devices running 4.0.

If you're unlucky / things to watch out for:
Getting a successful build after the above change is the ideal scenario.  If you're unlucky, your app may require a little more work to port.

As an example, I've recently ported the open source market app 'Kitchen Timer'.  This presented two problems:

1. The NumberPickers:
The current version of Kitchen Timer uses NumberPicker widgets, which were not officially available pre-ICS.  These are now available since api 11, but in order to get these to update their style, I had to swap out each use of the custom NumberPicker instances and replace them with android.widget.NumberPicker, as well as update a couple of api calls.

An unfortunate effect of a change like this is that the app is now no longer compatible with pre-ICS.

2. The ActionBar
As I've quickly found out, there are no option menus in ICS, which have been since replaced by either a items on an ActionBar, or an overflow button on the button bar for backwards compatibility reasons.
This is fine, except that the ActionBar can take up layout space that your original design may not have accounted for.

I ended up removing two buttons from the Kitchen Timer to allow all three timers to be controlled with one button.

Summary:
That's about it, if you're lucky, just increasing your targetSdkVersion and rebuilding with the latest sdk will be enough to instantly modernise your app.

Hopefully by having read the above you can save time by jumping over some of the more common pitfalls associated with this process.

Extra Reading:
You can also look at taking ports further with some of Jake Wharton's fantastic backwards compatible libraries such as ActionBarSherlock, and ViewPagerIndicator.

Updated KitchenTimer source: https://github.com/brk3/kitchentimer
Market Link: https://market.android.com/details?id=com.bourke.kitchentimer

Saturday, October 29, 2011

Writing multitouch applications on Android 
with android-multitouch-controller

Like many things in Java/Android, writing multitouch enabled applications in Android can require quite a bit boilerplate code.  It also requires quite a bit of knowledge of the workings of multitouch, and how the points are tracked by the api.  This can make it tedious for new developers to get started with implementing this kind of functionality.

Much of the above can be avoided by using Luke Hutchison's android-multitouch-controller.

It still requires some boilerplate, however it takes care of a lot of the headaches around properly implementing operations such as pinch-to-zoom, pinch-to-scale, pinch-to-rotate, etc., and lets you get on with the rest of your app.

I've found it quite extendable, and it provides a good basis for those needing to implement a more advanced multitouch tracking.  I've also made use of it extensively for my app RoidRage, so thought I could share some examples on how to get started with using this library.

Overview:
First, grab the latest source from github:

$ git clone git://github.com/brk3/android-multitouch-controller.git

This is my fork from Luke's original repository on Google code.  It contains some extras and refinements, which I'll get into another time.

I may get round to making it into a library project but until then it's relatively easy to just drop into your own code:

$ cp -r android-multitouch-controller/src/org $MYPROJECT/src

You then need to implement the MultiTouchObjectCanvas interface from the View you want to be multitouch enabled, and override a couple of methods:

import org.metalev.multitouch.controller.MultiTouchController;
import org.metalev.multitouch.controller.MultiTouchController.MultiTouchObjectCanvas;
import org.metalev.multitouch.controller.MultiTouchController.PointInfo;
import org.metalev.multitouch.controller.MultiTouchController.PositionAndScale;
import org.metalev.multitouch.controller.MultiTouchController.MultiTouchEntity;
import org.metalev.multitouch.controller.MultiTouchController.ImageEntity;

public class DemoView extends View
        implements MultiTouchObjectCanvas<MultiTouchEntity> {

    private MultiTouchController<Object> multiTouchController =
        new MultiTouchController<Object>(this);

    public DemoView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void selectObject(MultiTouchEntity e, PointInfo touchPoint) {

    }

    @Override
    public boolean setPositionAndScale(MultiTouchEntity e,
            PositionAndScale newImgPosAndScale, PointInfo touchPoint) {
        return true;
    }

    @Override
    public void getPositionAndScale(MultiTouchEntity e,
            PositionAndScale objPosAndScaleOut) {
    }

    @Override
    public MultiTouchEntity getDraggableObjectAtPoint(PointInfo pt) {
        return null;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return multiTouchController.onTouchEvent(event);
    }

    // rest of my code..
}

The first thing you'll probably notice about the above is the template type T given to the MultiTouchObjectCanvas.
This can be any class you want, depending on how you want to interact with your canvas. In most cases, people will probably be interested in manipulating images, so my fork of this library contains two new classes, MultiTouchEntity, and a subclass called ImageEntity.

ImageEntity is a small subclass of MultiTouchEntity, which can be used to hold an image to be managed by multitouch.

Let's see how we can add some images to our above example using ImageEntity:

import org.metalev.multitouch.controller.MultiTouchController;
import org.metalev.multitouch.controller.MultiTouchController.MultiTouchObjectCanvas;
import org.metalev.multitouch.controller.MultiTouchController.PointInfo;
import org.metalev.multitouch.controller.MultiTouchController.PositionAndScale;
import org.metalev.multitouch.controller.MultiTouchController.MultiTouchEntity;
import org.metalev.multitouch.controller.MultiTouchController.ImageEntity;

public class DemoView extends View
        implements MultiTouchObjectCanvas<MultiTouchEntity> {

    private MultiTouchController<MultiTouchEntity> multiTouchController =
        new MultiTouchController<MultiTouchEntity>(this);

    private ArrayList<MultiTouchEntity> mImages = 
        new ArrayList<MultiTouchEntity>();

    private static final int[] IMAGES = { R.drawable.m74hubble, R.drawable.catarina, 
        R.drawable.tahiti, R.drawable.sunset, R.drawable.lake };

    public DemoView(Context context, AttributeSet attrs) {
        super(context, attrs);

        Resources res = context.getResources();
        for (int i = 0; i < IMAGES.length; i++) {
            mImages.add(new ImageEntity(IMAGES[i], res));
            mImages.get(i).load(context, 50.0f, 50.0f);
        }
    }

    @Override
    public void selectObject(MultiTouchEntity e, PointInfo touchPoint) {
        if (img != null) {
            // Move image to the top of the stack when selected
            mImages.remove(img);
            mImages.add(img);
        } else {
            // Called with img == null when drag stops.
        }
        invalidate();
    }

    @Override
    public boolean setPositionAndScale(MultiTouchEntity e,
            PositionAndScale newImgPosAndScale, PointInfo touchPoint) {
        boolean ok = ((ImageEntity)img).setPos(newImgPosAndScale);
        if (ok)
            invalidate();
        return ok;
    }

    @Override
    public void getPositionAndScale(MultiTouchEntity e,
            PositionAndScale objPosAndScaleOut) {
        objPosAndScaleOut.set(img.getCenterX(), img.getCenterY(), 
            (mUIMode & UI_MODE_ANISOTROPIC_SCALE) == 0,
            (img.getScaleX() + img.getScaleY()) / 2, 
            (mUIMode & UI_MODE_ANISOTROPIC_SCALE) != 0, 
            img.getScaleX(), img.getScaleY(),
            (mUIMode & UI_MODE_ROTATE) != 0, img.getAngle());
    }

    @Override
    public MultiTouchEntity getDraggableObjectAtPoint(PointInfo pt) {
        float x = pt.getX(), y = pt.getY();
        int n = mImages.size();
        for (int i = n - 1; i >= 0; i--) {
            ImageEntity im = (ImageEntity) mImages.get(i);
            if (im.containsPoint(x, y))
                return im;
        }
        return null;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return multiTouchController.onTouchEvent(event);
    }

    // rest of my code..
}

Again for simple purposes it's possible that the amount of boilerplate could be stripped down further, but if you have a read through it's fairly self explanatory.

The above code has been mostly extracted from the demo application that ships with the library, 'MTPhotoSorter', which I recommend at least building and installing this to get a feel for what's possible with the library.

That's about it for now, I can follow up this post with a few more tips and examples if anyone finds it useful.

Sunday, July 17, 2011

First app, first release!


It's been about two and a half months since I started work on my first Android app, and I'm proud to say I've finally managed to get it out.

Have to say I've really enjoyed it from start to finish, and hope it will be the first of many :) Here's hoping it actually works for people and at least someone manages to have some fun with it.

If you're reading this for the first time thanks for stopping by, I'll try to keep update here a little more frequently with news of any updates.  For the dev interested folk I may also try and find time to write up on some of the trickier issues I encountered along the way.

That's all for now, check out my app and leave it 5 stars, you know you want to ;)

Available in Android Market

Wednesday, February 23, 2011

The importance of uniform UIs


It's a little ironic that given Android is one of the easier platforms to pirate software, it's also one of the platforms I don't mind paying for software. An app I'll pay for generally meets the following criteria:



  • I use it multiple times a day, and will continue to over a long period.
  • The developer appears responsive to users regarding features/support.
  • It's a reasonable price (in my opinion this is generally less than €1.99).
  • It looks good and has an intuitive interface.

The last point is probably the main one and something I think is unfortunately lost on a lot of developers. It's fair to say many programmers aren't good graphic designers. Good is obviously also a subjective term.

Enter the Android UI Guidelines.  Google have taken the time to concisely write up guidelines on how an app should look and feel, the benefits of which should be obvious.
Do they stifle creativity or prevent an app from looking distinct? No. But what they will do is help users feel at ease using your app from the get go, and help them run great on as many different devices as possible.

It really frustrates me to see people recommending an app so highly only to download it on my Desire HD to be met with stretched out low res icons, ignoring the use of the dedicated Android hardkeys with ever present settings menus etc.

Apple have a very similar set of guidelines for developing iOS apps, yet they seem to suffer far less from this issue. I don't think the old cliche of fragmentation is at fault here at all, more developers not wishing to follow standards.

Perhaps Google could be a little better at regulating the marketplace also, particularly as the Android tablets start to launch.

Doing it right, the recently redesigned Twitter for Android.

Tuesday, February 15, 2011

"Experts" Exchange annoyances

Google have just released a nice extension for Chrome for filtering or blocking certain sites from appearing in search results. (http://bit.ly/gTADhE)

What makes this different from just a hosts file or adblock is that it takes the most blocked sites and sends them back to Google for analysis.

The first thing that came to my mind was the numerous crappy one click hoster "link" sites that seem to consistently manage to make it into the top search results as long as you search rapidshare+anything.

Most people however seem to be glad that this might finally mean the end of sites such as experts-exchange.com.  This seems to appear in a surprisingly frequent number of programming related searches, only to infuriate the answer seeker with a subscription fee for the answer.

What a lot of people still don't know though, is if you scroll right down to the bottom on these pages, the answer was there all a long for free :)

TL;DR version:
Scroll down to the bottom on experts-exchange for the answer.
#table1 { width: 49%; float: left; } #table2 { width: 49%; float: right; }