Amazon Prices for Ireland – Chrome Extension

01Jul10

Even though amazon.co.uk ship to Ireland they don’t price their products in Euro. It’s not until we reach the very last page of the order process that we see the full Euro value of the order with Irish VAT applied.

What this plugin does is update each product page with the equivalent Euro price. The GBP to EUR rate is retrieved in real time from Yahoo Finance. The Irish VAT rate of 21% is included except on books which are exempt from VAT.

Now available on Chrome.

The original Firefox version is still available here.

UPM 1.6 Released

23Jun10

Universal Password Manager 1.6 has just been released. It can be downloaded here.

From the release notes…

  • Added the ability to export/import from/to a CSV file
  • Added a random password generator
  • Removed the dependency on JCE Unlimited Strength Jurisdiction Policy Files
  • Added support for using HTTPS URLs with database sharing
  • Set focus on the password field on all enter master password dialogs
  • Added Spanish translation (courtesy of Victor Alfonso Pineda)
  • Resize the “Notes” text area when the Account Details dialog is resized

UPM 1.1 Released

30Mar10

With this release UPM on Android reaches feature parity with the desktop version. The main features included with this release are,

  • Shared Database support
  • Delete Database support
  • Lots of bugs fixes and improvements

The shared database feature was the primary focus of 1.1. With this it’s now possible to sync your password database with the desktop version. For full details on how to setup and use database sharing see the user guide.

You can upgrade from the Android Marketplace or download the APK directly from Sourceforge.

If you have any problems post please use the Help Forum.

UPDATE: When you upgrade you may get an error telling you UPM is no longer available when you try to start it. To fix this you should restart your phone and recreate any shortcut you have to UPM on your Home screen.

For those who are interested this seems to be a problem with Android 1.5 and how it handles a change to the main Activity after an upgrade. If you have any experience or details on this problem I’d be interested hear about it.

Bookmarks for 2010-03-05

04Mar10

Randy Pausch: Really achieving your childhood dreams | Video on TED.com
Randy Pausch shares his story. Amazing stuff. I can only imagine what it would have been like to work (or play) with this man.

Oracle DBMS_CHANGE_NOTIFICATION
An interesting way of subscribing to events in an Oracle db. Could be used as an alternative to triggers where the work to be carried out is not possible from a trigger or needs to be done async to the trigger.

QuickSharp
MonoDevelop
SharpDevelop IDE for C#
A few opensource C# IDEs.

Multipart form upload on Android

18Feb10

Android 1.5 includes Apache HttpClient 4 for the purposes of making HTTP requests. Unfortunately HttpClient 4 (or the version included with Android anyway) doesn’t appear to support multipart form uploads.

As it happens writing a solution from scratch (at the HTTP layer that is) is pretty straight forward. The main challenge is understanding how the POST request should be structured. Once you know that it’s simply a matter of putting all the pieces together.

The connection is managed using the java.net.HttpURLConnection class. There’s a few properties that need to set on the connection to ensure it can be written to and read from,

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);

A special header informs the server that this will be a multipart form submission.

conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=xxxxxxxxxx");

The “boundry” can be any string. In this example it’s “xxxxxxxxxx”. It’s used in the body of the request to seperate each field being submitted.

Next comes the main body of the request. Each field being submitted follows the same pattern. For example, to submit a text file called “helloworld.txt” with the contents “Hello World” and using the boundry string “xxxxxxxxxx” here’s what the request would look like,

--xxxxxxxxxx
Content-Disposition: form-data; name="filetoupload"; filename="helloworld.txt"
Content-Type: text/plain

Hello World
--xxxxxxxxxx--

The final -- is important. It tells the server it’s the end of the submission.

Here’s a full method that’s used to upload a file to a URL. It can optionally take a username and password which is used to perform BASIC authentication.

    public static void put(String targetURL, File file, String username, String password) throws Exception {

        String BOUNDRY = "==================================";
        HttpURLConnection conn = null; 

        try {

            // These strings are sent in the request body. They provide information about the file being uploaded
            String contentDisposition = "Content-Disposition: form-data; name=\"userfile\"; filename=\"" + file.getName() + "\"";
            String contentType = "Content-Type: application/octet-stream";

            // This is the standard format for a multipart request
            StringBuffer requestBody = new StringBuffer();
            requestBody.append("--");
            requestBody.append(BOUNDRY);
            requestBody.append('\n');
            requestBody.append(contentDisposition);
            requestBody.append('\n');
            requestBody.append(contentType);
            requestBody.append('\n');
            requestBody.append('\n');
            requestBody.append(new String(Util.getBytesFromFile(file)));
            requestBody.append("--");
            requestBody.append(BOUNDRY);
            requestBody.append("--");

            // Make a connect to the server
            URL url = new URL(targetURL);
            conn = (HttpURLConnection) url.openConnection();

            // Put the authentication details in the request
            if (username != null) {
                String usernamePassword = username + ":" + password;
                String encodedUsernamePassword = Base64.encodeBytes(usernamePassword.getBytes());
                conn.setRequestProperty ("Authorization", "Basic " + encodedUsernamePassword);
            }

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDRY);

            // Send the body
            DataOutputStream dataOS = new DataOutputStream(conn.getOutputStream());
            dataOS.writeBytes(requestBody.toString());
            dataOS.flush();
            dataOS.close();

            // Ensure we got the HTTP 200 response code
            int responseCode = conn.getResponseCode();
            if (responseCode != 200) {
                throw new Exception(String.format("Received the response code %d from the URL %s", responseCode, url));
            }

            // Read the response
            InputStream is = conn.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024];
            int bytesRead;
            while((bytesRead = is.read(bytes)) != -1) {
                baos.write(bytes, 0, bytesRead);
            }
            byte[] bytesReceived = baos.toByteArray();
            baos.close();

            is.close();
            String response = new String(bytesReceived);
            
            // TODO: Do something here to handle the 'response' string

        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }

    }

There’s nothing in this code particular to Android so it will work in any java application.