2009
08.13

I’ve had this idea for a new Android app in my head for a few days and today I finally took some time to implement it.

Quickcopy

Quickcopy is a little application that lets you store little text snippets that you otherwise need to type frequently. For example an email address or a difficult to type login to some website.

Whenever you need one of these snippets in an application you just launch Quickcopy, tap the snippet you need and go back to your application where you can then paste the snippet.

quickcopy1quickcopy2

The first version of Quickcopy is already available on the market. The features currently implemented are

  • Add entries via the menu button
  • Copy entry to clipboard by tapping
  • Edit entry by long pressing
  • Delete entry by long pressing
  • View instructions via the menu button

To download this application use the barcode scanner app to scan the following barcode or manually browse to Quickcopy in the Android Market app.

quickcopybar

2009
08.10

As a new feature in my Android app for the belgian mobile phone carrier Mobile Vikings I wanted to display contact names in the call history instead of the number. After a bit of googling I found that Android provides a few contentproviders holding the data I needed (contact names and phone numbers).

In a first attempt I retrieved all contacts with a simple query on the Contacts.Phone contentprovider, manually looped over the results and compared each number with the number I wanted to replace. This worked only when the number on the phone was stored exactly in the same format as the Mobile Vikings API returned it. If the Mobile Vikings api returned the number with the area code and the number was stored on the phone without the area code, no match was found.

After trying to figure out how I could get both numbers in the same format for comparison I figured out that Android provided a mechanism to do this. I could encode a phone number string into a filter URI provided by the Contacts.Phones contentprovider and use that filter to query on.

The code looks like this

	private String getContactNameFromNumber(String number) {
		// define the columns I want the query to return
		String[] projection = new String[] {
				Contacts.Phones.DISPLAY_NAME,
				Contacts.Phones.NUMBER };
 
		// encode the phone number and build the filter URI
		Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, Uri.encode(number));
 
		// query time
		Cursor c = getContentResolver().query(contactUri, projection, null,
				null, null);
 
		// if the query returns 1 or more results
		// return the first result
		if (c.moveToFirst()) {
			String name = c.getString(c
					.getColumnIndex(Contacts.Phones.DISPLAY_NAME));
			return name;
		}
 
		// return the original number if no match was found
		return number;
	}

The result looks like this

contactnames

2009
08.07

In one of my previous posts about Google Wave I mentioned a security issue concerning gadgets. I decided to do a little more research on this subject and to do some experiments on some publicly available gadgets.

The issue

Before I get into detail about the issue, you need to know how Google Wave gadgets work. Gadgets are little pieces of html/javascript code that run inside of a wave. The state of a gadget is shared among every participant of the wave. Developers can access variables in this state by executing wave.getState().get(’name_of_variable’). To save or update variables into the state the following function exists. wave.getState().submitDelta({’name_of_variable’: value})

This last function is what bothers me about the way gadgets work. The wave server accepts any data that submitDelta passes on without any form of validation or authentication. As javascript is a clientside interpreted language that is executed inside of the browser, any user with the firebug extension (or some other debugging app/plugin) can alter that running code and manipulate these gadgets in ways that are not intended.

Some examples

Google Wave developer sandbox users should be familiar with the public “Wanna buy a wave t-shirt” wave. It is a wave to show of a simple counter gadget that is explained in the gadget api docs. the gadget has a button and a label. Every time the button is clicked the value in the label is incremented by one. The code for handling the buttonclick looks like this.

function buttonClicked() {
     var value = parseInt(wave.getState().get('count', '0'));
     wave.getState().submitDelta({'count': value + 1});
}

As you can see the code retrieves the current value of ‘count’ from the state, stores it in a variable ‘value’ and then pushes that value+1 back to the server with submitDelta(). To try something out I placed a breakpoint just before the new value was submitted back to the server, changed the value of ‘value’ to ‘99999′ and hit continue. As expected, the wave server accepted my altered value and pushed the new state to every user currently participating that wave.

In the gadget api docs there is an auction gadget available which demonstrates the use of wave participants. This gadget lets wave participants place bids. The gadget displays the name and picture of the highest bidder together with the amount. The bidding code looks like this.

    function buttonClicked() {
      var viewerId = wave.getViewer().getId();
      var state = wave.getState();
      var bid = parseInt(document.getElementById('yourBid').value);
      var currentBid = parseInt(state.get(viewerId, '0'));
      if (bid > currentBid) {
        delta = {};
        delta[viewerId] = bid;
        state.submitDelta(delta);
      }
    }

What I did here was : I placed a breakpoint after the viewerId was retrieved from the wave. This viewerId held my email address because I was the one viewing the gadget. I replaced this email-address with an address from another participant inside that wave and changed the value of the bid variable to 99999. After I hit continue to execute the remaining code, again my changes were accepted by the server and I had successfully placed a bid in someone else’s name. The updated state was once again pushed onto every wave participant’s gadget and they all got to see my victim’s name, picture and 99999 bidding amount.

Solution

What can developers do to prevent this abuse? Well, as long as we have no control over how the waveserver validates input received from submitDelta() there is not much we can do. If you really need serverside validation for your gadget then I think it is best to avoid the shared state and use an external server (on which you can execute validation code) to store your shared variables.

Some people may not consider this to be a security issue and will say it is just the way wave gadgets are supposed to work. As this may be true, I hope it’s not because this would limit the use of gadgets to fairly trivial appliances.

I have no idea what the Google Wave developers think of this. I am surely going to report this issue in the Google Wave issue tracker and keep you updated on the status.

2009
08.06

This is part 3 of my Google Wave developer sandbox review. Part 1 and part 2 were both an introduction to some wave concepts. In this part we’ll be taking a look at the Google Wave Gadget API by writing a simple rating gadget. If you don’t know what gadgets are or what they do, please read my previous post here.

The gadget

The gadget I have written is supposed to be used when you want wave participants to rate something inside your wave (or the wave itself). You simply add the gadget to your wave, set the title and you’re good to go. I also wanted the gadget to have a settings menu that was only visible by the person who added the gadget to the wave.

Rate me gadget

The full source of this gadget is available at http://vbsteven.be/wave/rateme.xml . You can add the gadget to one of your own waves by choosing extensions/add gadget in the debug menu and then pasting the url into the textbox at the bottom of the dialog.

Basic code layout

Google Wave Gadgets are simple xml files with html and javascript code embedded in them.

As you can see here, this is just an xml file with some wave-specific settings at the top like the title and the height of the gadget. Then there is a CDATA block which contains all our code. At the top of this block you’ll find the javascript includes (for JSON) and some javascript functions which define the behaviour of the gadget. At the bottom of the CDATA block you’ll find the HTML code which define the UI elements. When you want to use other external files (CSS for example), I suggest you link to those files at the top of the CDATA block.

Initializing

To initialize our gadget we need to define and register an init() method. This method will be called every time the gadget is loaded (for example when someone opens the wave). This method does not need to be long and is basically just to register the necessary callbackfunctions. In this case just the callback function that will notify our gadget when the shared state has changed is registered. As the shared state may not be fully initialized it is important that you don’t access the state object in this function.

function init() {
	if (wave) {
		wave.setStateCallback(stateUpdated);
	}
}
gadgets.util.registerOnLoadHandler(init);

Manipulating state

It is very important to notice that the state of a gadget is shared among all participants of the wave. When one user performs an action that changes the state, this change will be reflected on every instance of that gadget that is currently open. The state of a gadget is represented by a state object that contains key-value pairs. Values can be retrieved from the state with the get(’key’) function and stored in the state with the submitDelta({’key’ : value, ‘key’ : value}) function.

Currently the state object only works with strings. So it is not possible to store complex objects or arrays into the state. When you do need to store a complex object or an array, you can convert the object to a JSON string for storage and parse that string back to an object after you get it back from the state. The documentation on the get and submitDelta functions wasn’t very clear to me at first, which led to me losing some time trying to figure out why my array of voters wouldn’t show up into the state object (thank you firebug).

You can find some examples of how to manipulate the shared state in my vote() and saveSettings() functions.

		function vote() {
			var total = parseInt(wave.getState().get('total', '0'));
			var count = parseInt(wave.getState().get('count', '0'));
			var voters = JSON.parse(wave.getState().get('voters', '[]'));
 
			total += parseInt(document.getElementById("rating").value)
			count++;
			var rating = total/count;
			rating = rating.toFixed(1);
			voters.push(wave.getViewer().getId());	
 
			var voters_json = JSON.stringify(voters);	
 
			wave.getState().submitDelta({'count': count, 'total': total, 'rating': rating, 'voters': voters_json });
		}

Reacting to a state-change

If you registered your callbacks well, your stateUpdated() function should be called whenever a value changed in the shared state. This is the ideal time to get the updated state and update the UI. Most of the UI-related code should be in this function. Every time this function is called in my rateme gadget, I fetch the latest values from the state and update every UI component.

		function stateUpdated() {
			var p_rating = document.getElementById("p_rating");
 
			if (!wave.getState().get('count')) {
				p_rating.innerHTML = "rating : n/a";
				p_rating.innerHTML += "
votes : 0";
			}
			else {
				p_rating.innerHTML = "rating : " + wave.getState().get('rating');
				p_rating.innerHTML += "
votes : " + wave.getState().get('count');
			}
 
			var title = document.getElementById("h2_title");
			title.innerHTML = wave.getState().get('title', 'Rate me');
 
			if (wave.getViewer() == wave.getHost()) {
				// current user is the user that added the gadget to this wave
				var p = document.getElementById("p_settings");
				p.innerHTML = '<a onclick="showSettings()" href="#">show settings</a>';
			}
		}

Getting user info

Because I wanted to make a difference between the original user who added the gadget to the wave and the other users I had to have access to some user info. Luckily the wave object supplied by the API provides us with the necessary data. The wave.getParticipants() function returns an array of all users in the wave, getViewer returns the current viewer and getHost() returns the user that added the gadget to the wave. As you can see in the above code snippet, a simple equals check is enough to authenticate the user as the host and display the link tag.

Security

Ofcourse the authentication above looks easy. and it is,  but it also is a security issue. When you ask  any security specialist about AJAX apps, there is a very big chance he will say “Never trust the client”. And he’s right, as all javascript code is run inside the browser, any kid with firebug installed can alter that code. If you take a look at the above code sample you’ll see that a simple if (true) is enough to gain access to the settings panel.

In a normal webapp this wouldn’t be such a big deal. Even when the malicious user gained access to the settings by messing with the clientcode, the server would still verify every action and will prevent unauthorised actions. Because our gadget runs inside of google wave and we have no control over the code running on google servers, there is no way to validate the user input on the server and therefore the server will accept this user input.

Because of this issue there is no real safe way to handle authentication so every user inside the wave is able to alter the shared state, making gadgets unsafe for sensitive data.

My opinion

In my opinion the Google Wave gadget api is very easy to work with. As gadgets are written in plain html/javascript, webdevelopers with some experience in js won’t have a problem writing apps for the Google Wave platform. Although I’m still rather new to javascript (only did some basic ajax programming in school), I didn’t have any major problems while writing this little gadget. The api itself is very simple, straightforward and well-documented with very good code-samples.

On the downside, because of the security issue mentioned above I’m afraid gadgets will be limited to simple apps handling unimportant data (games, polls, etc).

2009
08.03

As the first part of my Google Wave developer sandbox review handled basic usage, this article will be about some more advanced features currently in the Google Wave developer sandbox.

Attachments

Adding attachments to a wave works similar to adding attachments to an email. You click the attachment button, select one or more files, click submit and voila. Your files are uploaded to and displayed inside the wave.

You can attach almost any filetype to a wave. In case you have one or more image files the thumbnails of these files are displayed inside the wave. When you click on a thumbnail the larger image is displayed on a dark background. When there are multiple images added to a wave you can browse them by clicking the image button at the bottom and selecting “view as slideshow”.

Screenshot-Steven (12+) - Google Wave - Google Chrome

Screenshot-Steven (12+) - Google Wave - Google Chrome-1

When uploading other filetypes, Google Wave tries to guess the contenttype of each file in order to display a fitting icon. This feature seems to work quite well. When I added a .air installation file to a wave, the wave correctly identified that file as a zip file and displayed the corresponding icon. After adding a pdf file, a preview of the file is displayed. Clicking on a non-image file in a wave results in a download of that file.

When you have the correct Gears plugin for your browser you should be able to add attachments to your waves by dragging them from your desktop into the wave. Because this feature didn’t work out of the box with any of my browsers and I didn’t have the time to figure it out, I decided to leave it for now.

Gadgets

Gadgets are small pieces of software you can attach to a wave to add custom functionality. Gadgets can be as simple as a counter that counts how many times a button has been clicked or as advanced as an embedded map displaying the current GPS location of the wave users.

Screenshot-Steven (14+) - Google Wave - Google Chrome

The state of a gadget is shared among wave users. This means that when one user changes the state of a gadget, this change is visible for every user inside that wave. For example when a Google Maps gadget is embedded in a wave and user A is zooming in on a city, the gadget on user B’s screen will synchronize and automatically zoom in on the same city.

Adding a gadget is currently done via the debug menu. After choosing debug/extensions/add gadget, a popup is displayed with a list of available gadgets and a textbox to add custom gadgets stored somewhere on the net. Google Wave has a few default gadgets including : a chess game, a Google Maps gadget and a “Who is coming” gadget. At the moment I have not tried to add some custom gadgets yet, I will see how that goes when I write my first own gadget one of these days.

Robots

Robots are a little different from gadgets. A robot is an automated participant in a wave that interacts with it just like normal users do. You add a robot to a wave just like you would add another user. First you add the robot to your list of contacts (each robot is identified with a unique email address) and then you start a new wave with it or add it to an existing wave. Robot’s can interact with wave users in all sorts of ways. They can reply to messages, edit messages, create waves, add attachments, …

There are already quite a few nice robots out there for use with the Google Wave developer sandbox. Some of them are developed by the Google developers (Tweety – a twitter robot, Spelly – a spell checker, Stocky – a stock checker) and some other robots are developed by external developers (Roshambo – a rock paper scissors robot – is currently the only non-google robot I have tried).

Debug menu

The Google Wave developer sandbox comes with a handy debug menu. This menu has items for developers to check network information, check wave information, set the window size (not working in chrome dev build here atm) and generate/throw exceptions for debugging. At the moment also non-developers need this menu as it is the only way (that I know of) to add gadgets to a wave.

I haven’t had the chance to check out how most of these functions work as I haven’t written any code for the platform yet. Once I start developing a gadget and a robot I will probably use this menu a little bit more and have something more to say about it.

Wrapping up again

This post wasn’t very long as it was just an introduction to a few advanced concepts in the Google Wave developer sandbox. Today I will start experimenting with the gadget and robot API and in the next post I will talk about my experiences developing for the Google Wave platform. I already have an idea for the gadget but still no clue for the robot. Idea’s are suggestions are still welcome. You can leave a comment or contact me via my new contact page.