jasonfry.co.uk

Understanding Android Intents For The Non-Technical Audience

Who is This Post For?

This post is aimed at the sort of people within mobile app teams that aren't technical, both designers and the (boring) business type :p

If you're after something more technical then check out Google, the Android documentation or Matt Oakes' crazy slides from Android Manchester's April 2011 Meetup.

What is an Intent?

Looking up 'intent' in the dictionary points you to this incredibly descriptive definition:

“intention or purpose”

That's not hugely helpful, but it leads us to this definition of 'intention':

“a thing intended; an aim or plan”

So how does this translate to defining a definition for the context of Android? Essentially, an Intent is the way your app can say to underlying Android system “I want (or intend...) to carry out a specific task”.

What Are Intents Used For?

Intents are used all over the place in Android in one way that won't mean anything to someone that isn't an Android developer, they allow your app to explicitly move between different Activities and start Services. If you have no idea what that means then fear not, you weren't meant to.

But more importantly for you they are also used to allow different apps to share their features with each other. What this means is that you can have (for example) your own video blogging app and you don't have to write your own video recorder, you can simply say to the system “I want to record a video” and the phone will either use the built in video recording app, or another video recording app that the user has downloaded. Once the video has been recorded it is then passed back to the original app. This makes a great user experience because far as the user is concerned they never left your app.

Another example could be that you have a piece of content in your app and you would like your user to able to share it in some way. You can just say to the system that you wish to share something and the user will be presented with a list of different options for sharing, e.g. sms, twitter, facebook, etc.

If there is just one thing that can handle your intent then that thing will just be used by default, but if there are several different apps that can handle your intent then the user is given a list of things to choose from, and also has the option of setting it as the default action.

What Should My App Utilise?

This question is kind of hard to answer without actually knowing exactly what your app does. But what you shouldn't do is rely on 3rd party apps for your app to function. Say for example that you are a large supermarket and you are building your own Android shopping experience, don't rely on the user having a barcode scanner installed for the app to function. If you have the time and the budget to build your own scanner as part of your app then definitely do it, but don't force your users to use it, make your barcode scanner respond to barcode scanning intents exactly the same way all the other barcode scanners do. Some users may prefer to use their own barcode scanning app that plays “Lost In The Supermarket” by The Clash in the background. Or something.

However you shouldn't reinvent the wheel unless you need to. If the device has a camera, then it will have built in intents for taking photos and recording videos, you don't need to make your own camera. This also helps the user as it means they get a consistent camera experience no whether they are taking a photo of their brother passed out on the floor to post on their father's Facebook wall or taking an arty photo of Brighton West Pier at sunset to post on their flickr account.

Most Android devices also include maps. So say you want to give the user directions to your nearest store, don't reinvent this in your app. Push them out to the built in google maps app, or any other app that they might have installed that can handle giving directions!

What Should My App Offer?

A classic example would be to offer to handle web addresses. So say you are a large retailer, and someone clicks on a link that directs them to a product page on your website, your app can offer to handle that request so rather than displaying the webpage it could display the product in your app.

You could also define your own specific actions. Again, say you are a large retailer, you can create specific intents that might let other apps add products directly to your basket. For example, a price comparison app could list several different retailers to buy a certain item, and in their app they could place a button that could allow the user to buy the item using your app. If you do go down this route, make sure these things are well documented!

Bonus

Intents don't just have to be used to do something in the foreground, they could trigger something to happen in the background. A great example of this is the last.fm app. The last.fm app can respond to specific intents that tell it that an app would like to scrobble a song! The last.fm app then handles the scrobbling directly without the user having to sign into last.fm in another app.

Conclusion

Intents are very powerful and if used wisely they can let you tie several different apps together into one lovely experience!

Disabling Overscroll in Gingerbread

The overscrolling in Gingerbread isn't particularly pretty, and as it's enabled by default it can end up spewing a large orange pile of crap over your otherwise pretty application. But how do you disable it whilst still keeping your app compatible with versions of Android prior to Gingerbread?

Place this class in your code somewhere:

import android.view.View;

public class OverScrollDisabler
{
	public static void disableOverScroll(View view)
	{
		view.setOverScrollMode(View.OVER\_SCROLL\_NEVER);
	}
}

And then after instantiating your view, place this piece of code in your class:

if(Build.VERSION.SDK\_INT >= 9)
{
	OverScrollDisabler.disableOverScroll(myView);
}

I'll be adding OverScrollDisabler into uk.co.jasonfry.android.tools very soon, so keep and eye on the github repo.

Enjoy!

[edit]It's been added!

My Morning With Git and Submodules

I spent this morning moving a project over from svn to git and setting up a git submodule for a library project. I spent far too long doing it and made far too many silly mistakes that cost me lots of times. So I am going to outline below my steps for setting up a new project with git, and then adding an existing submodule. This is a long way from a definitive guide to git, I expect you to already have a basic understanding of git. I wouldn't even call it a complete guide to setting up a new project with git, it's more of a don't forget these steps and do it in this order sort of thing.

If you've not already installed git, you probably wanna do that first. Visit the git website to download the latest version.

Just a warning, you will be using the command line so a little bit of basic command line knowledge is needed, i.e. you should know how to move around directories, move files, delete files, nano/vim etc. I've never found setting up a new git project using a git GUI works very well so I tend to do all my setup with the command line tools and then use gitbox once everything is ready.

So the first thing you're gonna want to do is make a folder for your project... (obviously). Then change to that directory on the command line. Now run the command

git init

This will turn this folder into a git repo. If you have already got a project setup on a server then don't create a new git project, instead clone the repository

git clone username@server:project/path

So if you were me and you wanted to clone my android tools repo from github you would run

git clone git@github.com:fry15/uk.co.jasonfry.android.tools.git

Notice that the username for github is always git@github.com, this is just the way they have there system setup. You can find the url to your github project on your github project page and if you've setup your own git server then you should know the address...

Now add all your files to the folder.

The next step is VERY important and one I always forget to do, there will be certain files you don't want to track, e.g. compiled binary files. Make a new file in your project root called '.gitignore' and add the file and folder names you want to ignore to this file. For more information check out the gitignore documentation or have a look at these templates. Why is this so important to do first? Because if you did this after you had added files then git will continue to track files that have been added even if .gitignore is saying to ignore them.

Now you'll want to let git know what files to watch, the likely scenario is all the files, excluding the ones you've put in the gitignore file. Run this command

git add .

Now you'll want to commit these changes locally

git commit

Note the files haven't been pushed to a server anywhere yet...

If you started by cloning a remote repo rather than initialising a new one then you can skip this step. If you've got your own git server then I expect you to already have made a remote repo, if you're planning on using github then this page is your friend.

The key command is the one to add the remote server to the config

git remote add origin git@github.com:your-username/your-reponame.git

Once you've done that you'll want to push your changes to the server

git push origin master

Now you've got the basic setup done

Now for the submodule part. It's not actually that difficult, just run this command

git submodule add username@server:project/path local/path

So if you were me and you wanted a read write version of my 'android tools' project in a folder called 'sub' you would run

git submodule add git@github.com:fry15/uk.co.jasonfry.android.tools.git sub/uk.co.jasonfry.android.tools

Again, notice that the username for github is always git@github.com, this is just the way they have there system setup, if you've setup your own server then you should know what those details are... If you just wanted a read only version of a project rather than a read write version (this is most likely the case if you are adding an open source library) then use its http url in the command instead, so for example to get a read only version of my 'android tools' project you would run

git submodule add https://github.com/fry15/uk.co.jasonfry.android.tools.git sub/uk.co.jasonfry.android.tools

Now you will want to update your git project, so run

git commit -a -m 'my commit message'

to commit your changes. The command if you want to push to your remote server

git push

The submodule you have added is basically its own git repo, so when you make changes to the submodule, just change directory into its folder and do all the usual git stuff within that folder. If you update or pull in a change on your submodule then you'll need to let your main / master (whatever you want to call it) git project know that the submodule has updated, the master project just holds a reference to the submodule (the url and commit id).

Now say somebody else has checked out your master project repo, they will need to initialise the submodule you have added. This is done using

git submodule update --init submodule/path

If you have any git questions, I am not the person to ask. Google is your friend ;)

Android SwipeView Update

I have commited some updates to my Android Tools library on github. I've merged a fork by ohhorob that allowed you to set a custom width for your pages. I have also improved the way it handles layered touch events, and added support for vertically scrolling child views.

Documentation is in the same place: jasonfry.co.uk/android/tools/doc

Download the jar

If you would like to have a play with a demo application:

QR Code - Download SwipeViewDemo.apk

Download demo application source

Android Overscroll: Revisited

After yesterday's post I continued to look into Android's new Overscroll functionality introduced in Gingerbread and discovered some more interesting things. The functionality to make a a view scroll beyond its limits and then bounce back (almost exactly like iOS) is sort of built into the framework, but just hidden. I'm not sure exactly why it has been built like it has been, but I will give a few guesses after an explanation of what is actually going on, but first: DEMO!

So What's There and What Isn't?

I'm glad you asked… If we look into the ViewConfiguration class' source we find two variables of interest: OVERSCROLL_DISTANCE and OVERFLING_DISTANCE. These two variables tell the framework how much a view should be able to scroll beyond its limits. They are hard coded in and there are no methods available to set your own custom ones. OVERSCROLL_DISTANCE is set to 0 (!?) and OVERFLING_DISTANCE is set to 4.

For those that don't know, the ViewConfiguration class holds a set of values that Android uses to store the default timeouts / distances etc for certain UI behaviours. It also does some internal scaling and calculations based on screen density etc. If you're interested, have a look at the source

So with OVERSCROLL_DISTANCE set to 0, the view will never move beyond its limits, but you can do something fairly simple to achieve this behaviour.

In complicated terms, just extend the view you wish to use normally, (e.g. ListView) and override the overScrollBy method. Then within the overScrollBy method body, simply call the super.overScrollBy but with your own values for maxOverScrollX and/or maxOverScrollY. If you're gonna do this, make sure you scale your values based on the screen density.

Confused? Have a code sample:

public class BounceListView extends ListView
{
    private static final int MAX\_Y\_OVERSCROLL\_DISTANCE = 200;
    
    private Context mContext;
    private int mMaxYOverscrollDistance;
    
    public BounceListView(Context context) 
    {
        super(context);
        mContext = context;
        initBounceListView();
    }
    
    public BounceListView(Context context, AttributeSet attrs) 
    {
        super(context, attrs);
        mContext = context;
        initBounceListView();
    }
    
    public BounceListView(Context context, AttributeSet attrs, int defStyle) 
    {
        super(context, attrs, defStyle);
        mContext = context;
        initBounceListView();
    }
    
    private void initBounceListView()
    {
        //get the density of the screen and do some maths with it on the max overscroll distance
        //variable so that you get similar behaviors no matter what the screen size
        
        final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
            final float density = metrics.density;
        
        mMaxYOverscrollDistance = (int) (density \* MAX\_Y\_OVERSCROLL\_DISTANCE);
    }
    
    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) 
    { 
        //This is where the magic happens, we have replaced the incoming maxOverScrollY with our own custom variable mMaxYOverscrollDistance; 
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxYOverscrollDistance, isTouchEvent);  
    }
    
}

And now just use that custom view wherever you would normally have used the standard view!

As promised, some guesses as to why this is happening. My first thought is that the developers had no intention of exposing this functionality and that it is simply meant to be used for the small springback you get after an overfling (remember where OVERFLING_DISTANCE was set to 4). But then is that was the case why set OVERSCROLL_DISTANCE to 0, why not just not include it if that was the case? Maybe they are planning something in the future? But if it was intended to be used, then why not create methods that let you set the overscroll distances for your views? Who knows…