The latest Android and Google Play news for app and game developers.
I'm thrilled to share the news that developers from over 70 countries submitted 1,788 entries to the Android Developer Challenge!
Here are a few facts that I thought were interesting. When we announced the Android Developer Challenge back in January, developers started submitting entries right away but it wasn't until the April 14 deadline approached that the flood really began. The rate of submissions spiked in the wee hours of Tuesday morning, reaching as high as 170+ submissions per hour.
What I find truly amazing is how global the interest in the challenge has been. Developers from the United States submitted one-third of the total applications while the rest came from countries such as Germany, Japan, China, India, Canada, France, UK, and many others. The entries are also very diverse representing many application areas, from games to social-networking applications, to utilities, to productivity and developer tools, and many more. On behalf of the Open Handset Alliance, I want to thank everyone who has submitted entries to the challenge. We look forward to reviewing all of them.
Now that the applications are in, over 100 judges will soon receive judging packets and laptops that we've preloaded with all the submissions, for a consistent, fair environment to judge the submissions. The majority of the judges are from member companies of the Open Handset Alliance, in addition to non-alliance mobile industry experts who have all graciously volunteered their time. I'd like to thank these judges too for all the time they will be putting into this.
In May, we'll be informing the 50 Semi-finalists who will be awarded $25,000 each. Until then, the team and I will have our hands full.
We have received a few inquiries regarding the judges who will be evaluating entries to the Android Developer Challenge (ADC). All Entries will be judged by a panel of experts in the fields of mobile devices, cellular telecommunications, software development, and/or technology innovation ("Judges"). Google will select the Judges from the member organizations of the Open Handset Alliance, Google and/or mobile experts.
As a reminder, the deadline for the Android Developer Challenge is April 14, 2008. We're really looking forward to seeing what you've created so make sure you submit in time. Good luck!
In the last article, we talked about using Linkify to turn wiki words (those that match a regular expression we defined) into a content: URI and defining a path to data that matched a note belonging to that wiki word. As an example, a matching word like ToDoList would be turned into a content: URI like content://com.google.android.wikinotes.db.wikinotes/wikinotes/ToDoList and then acted upon using the VIEW action from the Linkify class.
ToDoList
content://com.google.android.wikinotes.db.wikinotes/wikinotes/ToDoList
This article will examine how the Android operating system takes this combination of VIEW action and content: URI and finds the correct activity to fire in order to do something with the data. It will also explain how the other default links created by Linkify, like web URLs and telephone numbers, also result in the correct activity to handle that data type being fired. Finally, this article will start to examine the custom ContentProvider that has been created to handle WikiNotes data. The full description of the ContentProvider and what it does will span a couple more articles as well, because there is a lot to cover.
At a high level, the steps for Linkify to invoke an intent and for the resulting activity (if any) to handle it looks like this:
If this sounds complicated, it really is a simpler process than it sounds, and it is quite lightweight as well. Perhaps a more understandable statement about how it works might be:
Linkify is used to turn matching text into hot-links. When the user selects a hot-link, Android takes the data locator represented by the hot-link and looks for a data handler for that data locator. If it finds one, it asks for what type of data is returned for that locator. It then looks for something registered with the system that handles that type of data for the VIEW action, and starts it, including the data locator in the request.
The real key here is the MIME type. MIME stands for Multipurpose Internet Mail Extensions - a standard for sending attachments over email. The MIME type (which is the part Android uses) is a way of describing certain kinds of data. That type is then used to look for an Activity that can do something with that data type. In this way, ContentProviders and Activities (or other IntentReceivers) are decoupled, meaning that a given Content URI might have a different ContentProvider to handle it, but could still use the same MIME type meaning that the same activity could be called upon to handle the resulting data.
Using the above workflow, let's take a look at exactly how the process works in WikiNotes for Android:
First, Linkify is used to turn text matching the wiki word regular expression into a link that provides a Content URI for that wiki word, for example content://com.google.android.wikinotes.db.wikinotes/wikinotes/ToDoList.
When the user clicks on the wiki word link, Linkify invokes the VIEW action on the Content URI. At this point, the Android system takes over getting the Intent request to the correct activity.
Next, Android looks for a ContentProvider that has been registered with the system to handle URIs matching our Content URI format.
In our case, we have a definition inside our application in the AndroidManifest.xml file that reads:
<provider name="com.google.android.wikinotes.db.WikiNotesProvider" android:authorities="com.google.android.wikinotes.db.wikinotes" />
This establishes that we have a ContentProvider defined in our application that provides the "root authority": com.google.android.wikinotes.db.wikinotes. This is the first part of the Content URI that we create for a wiki word link. Root Authority is just another way of thinking about a descriptor that is registered with Android to allow requests for certain URLs to be routed to the correct class.
com.google.android.wikinotes.db.wikinotes
So, the whole definition is that a class called com.google.android.wikinotes.db.WikiNotesProvider is registered with the system as able to handle the com.google.android.wikinotes.db.wikinotes root authority (i.e. URIs starting with that identifier).
com.google.android.wikinotes.db.WikiNotesProvider
From here, Android takes the rest of the URI and present it to that ContentProvider. If you look at the WikiNotesProvider class and scroll to the very bottom - the static block there, you can see the pattern definitions to match the rest of the URL.
In particular, take a look at the two lines:
URI_MATCHER.addURI(WikiNote.WIKINOTES_AUTHORITY, "wikinotes", NOTES); URI_MATCHER.addURI(WikiNote.WIKINOTES_AUTHORITY, "wikinotes/*", NOTE_NAME);
These are the definitions of URIs that our ContentProvider recognizes and can handle. The first recognizes a full URI of content://com.google.android.wikinotes.db.wikinotes/wikinotes and associates that with a constant called NOTES. This is used elsewhere in the ContentProvider to provide a list of all of the wiki notes in the database when the URI is requested.
content://com.google.android.wikinotes.db.wikinotes/wikinotes
The second line uses a wildcard - '*' - to match a request of the form that Linkify will create, e.g. content://com.google.android.wikinotes.db.wikinotes/wikinotes/ToDoList. In this example, the * matches the ToDoList part of the URI and is available to the handler of the request, so that it can fish out the matching note for ToDoList and return it as the data. This also associates that match with a constant called NOTE_NAME, which again is used as an identifier elsewhere in the ContentProvider.
The other matches in this static block are related to forms of searching that have been implemented in the WikiNotes for Android application, and will be covered in later articles. Likewise, how the data is obtained from this matching pattern will be the subject of the next article.
For right now we are concerned with the MIME type for the URI. This is defined in the getType() method also in the WikiNotesProvider class (about half way through the file). Take a quick look at this. The key parts for now are:
case NOTES: return "vnd.android.cursor.dir/vnd.google.wikinote";
case NOTE_NAME: return "vnd.android.cursor.item/vnd.google.wikinote";
These are the same constant names we defined in our pattern matchers. In the first case, that of the all notes URI, the MIME type returned is vnd.android.cursor.dir/vnd.google.wikinote which is like saying an Android list (dir) of Google wiki notes (the vnd bit is MIME speak for "vendor specific definition"). Likewise, in the case of a NOTE_NAME match, the MIME type returned is vnd.android.cursor.item/vnd.google.wikinote which is like saying an Android item of Google wiki notes.
vnd.android.cursor.dir/vnd.google.wikinote
vnd.android.cursor.item/vnd.google.wikinote
Note that if you define your own MIME data types like this, the vnd.android.cursor.dir and vnd.android.cursor.item categories should be retained, since they have meaning to the Android system, but the actual item types should be changed to reflect your particular data type.
vnd.android.cursor.dir
vnd.android.cursor.item
So far Android has been able to find a ContentProvider that handles the Content URI supplied by the Linkify Intent call, and has queried the ContentProvider to find out the MIME types for that URI. The final step is to find an activity that can handle the VIEW action for that MIME type. Take a look in the the AndroidManifest.xml file again. Inside the WikiNotes activity definition, you will see:
<intent-filter> <action name="android.intent.action.VIEW"/> <category name="android.intent.category.DEFAULT"/> <category name="android.intent.category.BROWSABLE"/> <data mimetype="vnd.android.cursor.item/vnd.google.wikinote"/> </intent-filter>
This is the correct combination of matches for the VIEW action on a WikiNote type that is requested from the LINKIFY class. The DEFAULT category indicates that the WikiNotes activity should be treated as a default handler (a primary choice) for this kind of data, and the BROWSABLE category means it can be invoked from a "browser", in this case the marked-up Linkified text.
Using this information, Android can match up the VIEW action request for the WikiNotes data type with the WikiNotes activity, and can then use the WikiNotes activity to handle the request.
It's quite a trip through the system, and there is a lot to absorb here, but this is one of the main reasons I wanted to write WikiNotes in the first place. If you follow and understand the steps here, you'll have a good grasp of the whole Intents mechanism in Android, and how it helps loosely coupled activities cooperate to get things done.
In this case, we could have found another way to detect wiki words based on a regular expression, and maybe written our own handler to intercept clicks within the TextView and dig out the right data and display it. This would seem to accomplish the same functionality just as easily as using intents, so what is the advantage to using the full Intents mechanism?
In fact there are several advantages:
The most obvious is that because we are using the standard Intent based approach, we are not limited to just linking and navigating to other wiki notes. We get similar behavior to a number of other data types as well. For example, a telephone number or web URL in a wiki note will be marked up by Linkify, and using this same mechanism (VIEW action on the linked data type) the browser or dialer activities will be automatically fired.
It also means that each operation on a wiki note can be treated as a separate life cycle by our activity. We are not dealing with swapping data in and out of an existing activity - each activity works on a particular wiki note and that's all you have to worry about.
Another advantage is that we now have a public activity to handle VIEW actions in WikiNotes no matter where the request comes from. Another application could request to view a wiki note (perhaps without even knowing what kind of data it is) and our activity could start up and handle it.
The backstack is automatically maintained for you too. As you forward navigate through WikiNotes, Android maintains the history of notes visited, and so when you hit the back button you go back to the last note you were on. All this is free because we rely on the Android intents mechanism.
Finally, if you run WikiNotes for Android and then start DDMS to take a look at the Activity threads in the WikiNotes application while it is running, you can see that despite what you might think, letting Android manage the navigation is very efficient. Create a few linked notes, as many links deep as you like, and then follow them. If you follow links hundreds of notes deep, you will still only see a handful of WikiNotes activities. Android is managing the activities, closing the older ones as necessary and using the life cycle to swap data in and out.
This was a long article, but necessarily so. It demonstrates the importance of the Intents mechanism and to reinforce the notion that it should be used whenever possible for forward navigation, even within a single application. Illustrating this is one of the primary reasons I wrote WikiNotes for Android in the first place.
In the next article we will look deeper into the ContentProvider and examine how it turns a Content URI into a row (or several rows) of data that can be used by an activity.
This is the first in a series of technical articles about WikiNotes for Android, part of the Apps for Android project.
This article covers the use of Linkify to turn ordinary text views into richer link-oriented content that causes Android intents to fire when a link is selected.
Linkify: The Linkify class in the SDK is perfect for creating a wiki note pad. This class lets you specify a regular expression to match, and a scheme to prepend. The scheme is a string that, when the matched text is added, forms a Content URI to allow the correct data to be looked up.
For example, in our case we want to look for a regular expression match for a WikiWord (that is, a word with camel case and no spaces). Linkify can then turn this into a Content URI - something like >content://com.google.android.wikinotes.db.wikinotes/wikinotes/WikiWord which can then be used to locate the correct wiki page from a content provider.
>content://com.google.android.wikinotes.db.wikinotes/wikinotes/WikiWord
As a bonus, the Linkify class also defines several default matches, in particular it is able to turn web URLs, email addresses and telephone numbers into active links which fire Android intents automatically.
Linkify can be passed any TextView in your application, and will take care of creating the links and enabling their "clickability" for you.
Default Linkify: Using the set of default active link options is very straightforward - simply pass it a handle to a TextView with content in it, and the Linkify.ALL flag:
Linkify.ALL
TextView noteView = (TextView) findViewById(R.id.noteview); noteView.setText(someContent); Linkify.addLinks(noteView, Linkify.ALL);
and that's it. The Linkify.ALL flag applies all of the predefined link actions, and the TextView will be immediately updated with a set of active links which, if you select them, fire default intents for the actions (e.g. a web URL will start the browser with that URL, a telephone number will bring up the phone dialer with that number ready to call, etc.).
Custom Linkify: So what about our WikiWord? There is no pre-defined action for that, so it needs to be defined and associated with a scheme.
The first task is to defined a regular expression that matches the kind of WikiWords we want to find. The regex in this case is:
\b[A-Z]+[a-z0-9]+[A-Z][A-Za-z0-9]+\b
Obvious no? Well actually this is equivalent to the following description: "Starting with a word boundary (the \b) find at least one upper case letter, followed by at least one lower case letter or a numeric digit, followed by another upper case letter, and then any mix of upper case, lower case or numeric until the next word boundary (the final \b)". Regular expressions are not very pretty, but they are an extremely concise and accurate way of specifying a search pattern.
We also need to tell Linkify what to do with a match to the WikiWord. Linkify will automatically append whatever is matched to a scheme that is supplied to it, so for the sake of argument let's assume we have a ContentProvider that matches the following content URI:
content://com.google.android.wikinotes.db.wikinotes/wikinotes/WikiWord
The WikiWord part will be appended by Linkify when it finds a match, so we just need the part before that as our scheme.
Now that we have these two things, we use Linkify to connect them up:
Pattern wikiWordMatcher = Pattern.compile("\\b[A-Z]+[a-z0-9]+[A-Z][A-Za-z0-9]+\\b"); String wikiViewURL = "content://com.google.android.wikinotes.db.wikinotes/wikinotes/"; Linkify.addLinks(noteView, wikiWordMatcher, wikiViewURL);
Note that the \b's had to be escaped with double backslashes for the Java Pattern.compile line.
Linkify can be used multiple times on the same view to add more links, so using this after the Default Linkify call means that the existing active links will be maintained and the new WikiWords will be added. You could define more Linkify actions and keep applying them to the same TextView if you wanted to.
Now, if we have a WikiWord in the TextView, let's say MyToDoList, Linkify will turn it into an active link with the content URI:
MyToDoList
content://com.google.android.wikinotes.db.wikinotes/wikinotes/MyToDoList
and if you click on it, Android will fire the default intent for that content URI.
For this to all work, you will need a ContentProvider that understands that Content URI, and you will need a default activity capable of doing something with the resulting data. I plan to cover these in future blog entries (and soon). In fact, the whole Wiki Note Pad application is currently undergoing some clean up and review, and will then hopefully be released as a sample application.
The Android Developer Challenge is proceeding nicely. We're excited about the interest people have shown so far and have enjoyed talking to everyone working on new Android Apps.
As a quick reminder, the first phase of the challenge will be ending on April 14. In the Android Developer Challenge I, the 50 most promising entries received by April 14 will each receive a $25,000 award to fund further development. Those selected will then be eligible for even greater recognition via ten $275,000 awards and ten $100,000 awards.
Keep working on your applications, and be sure to post in the forums if you have any questions!