Tuesday 30 October 2012

Expandable Storage on Android Nexus Devices (Nexus 7 and Samsung Galaxy Nexus)

In this post, I am going to detail how to expand the storage of the device beyond the 8GB, 16GB or 32GB that comes built in to these devices. This process does not require modifying the ROM and does not require rooting the device; it is as simple as installing a single app.

The Nexus 7 tablet is fantastic; it has great dimensions, a good screen and comes at a fantastic price. The only real downside that I ever see about the device (or indeed, any "Nexus" device) is the lack of expandable storage; it has no micro SD card slot. The fact is though that it is very easy and inexpensive to add storage to your device. This is perfect for the long commute or going away on holiday scenarios where the on-board storage just doesn't cut it. Especially useful if you won't have access to your "media in the cloud" due to lack of wifi.

I figure now is a good time to highlight this for users who are tempted by the Nexus 7 tablet or other Nexus device but are hesitating due to the storage restriction. Also, since the announcement of the iPad Mini, now seems like a great time to highlight a strength of having Android as your OS!

This process allows you to connect a pen drive to your tablet or phone, and have any media stored on the pen become accessible to the mobile device. What does accessible mean? Well you be able to open the media straight from the device (streaming it, in the case of music or videos) or you can copy from the pen drive straight onto your device allowing you disconnect the pen drive whilst consuming your media.

With this process, your Nexus device can be expanded to support any size of expandable storage. I frequently use a 32GB pen drive, but I have also successfully used my 128GB Samsung Solid State Hard Drive too. (Note, external USB drives which require moving parts, like 2.5" external hard drives with platter hard drives don't work. This is because the device can't provide the hard drive with the power it needs over USB).

In order to provide more storage to the device, you will need the following:

  1. a Nexus device. I have personally tested this process on the Asus Nexus 7, as well as the Samsung Galaxy Nexus and presumably works all other Nexus devices. If you discover it to work on other devices, please leave a comment below.
  2. a pen drive - any old pen drive will do (in fact, even my 2.5" SSD drive worked a treat).
  3. an OTG micro-USB cable. Clicking on the link will take you to Amazon.co.uk where you can purchase one I recommend.
  4. the Nexus Media Importer application, available from Google Play. This cost just over £1 but trust me, it is well worth the money.

Setting up Pen Drive

Formatting

In order to have the pen drive readable from the tablet, you must ensure the pen drive is formatted correctly. As of version 3, the list of acceptable formats is:
  1. FAT16
  2. FAT32
  3. NTFS
If your pen drive is already in one of the formats above, you can move on. Otherwise, you will need to format your drive; you will only have to do this once but it is required.

I recommend you try NTFS first. FAT32 has a restriction on the file size meaning the largest size an individual file can be is 4GB. NTFS however, supports files up to a rather more impressive 16TB. Yes, TeraBytes. Realistically though, even 4GB is probably going to be fine for most things. Sure, a full HD movie might be bigger than 4GB; if you use FAT32 and need this kind of file size support, you will need to ensure your movie is chopped into parts, with each part smaller than 4GB.

If you experience any problems with NTFS, you can try FAT32 instead. If you are using Windows 7 (or later) and wish to use FAT32, you will want to make sure that you use a 3rd party add-on which supports formatting to Fat32 (like this app http://www.softpedia.com/get/System/Hard-Disk-Utils/FAT32format.shtml)

Adding Media Files

The files don't need to be named in any specific way, or held within a certain directory, so just copy them onto the drive. I would advise copying one file on first and testing it before spending a while copying your whole video library to find a problem later.

Connecting the Pen Drive

You have the USB OTG cable, so simply plug that into your device. Plug the pen drive into the USB slot on the cable. You really can't fail on this step! :-)

Launch the Nexus Media Importer app

If the app offers itself to launch automatically on a device being connected, it is a good idea to let it do this.



Otherwise, launch the app manually in the usual way you launch apps.




The "two arrows" icon in the upper-right of the app is used to trigger a re-scan of devices and might be required if your media isn't visible. Clicking on any of your media will present you with the ability to stream videos/music or open documents and pictures. The app itself is fairly straightforward but let me know if you have any specific questions/problems when trying this yourself.

Conclusion

There you go. The one single downside of the "Nexus" devices solved! Let me know how you get on if you're attempting this at home.

Friday 14 September 2012

Read X509 Certificate in Java

Reading an X509 certificate in Java can be done using the following code. I used it specifically for obtaining a reference to the Apple WWDR certificate, but generally speaking, this will work for reading any certificate in X509 format.

import java.security.cert.*;
import java.io.*;
import org.apache.commons.io.IOUtils;
import java.security.NoSuchProviderException;
import java.security.NoSuchAlgorithmException;
import java.security.KeyStoreException;


public static X509Certificate readWWDRCertificate(String keyFile) throws IOException, KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException, CertificateException
{
FileInputStream fis = null;
ByteArrayInputStream bais = null;
try 
{
// use FileInputStream to read the file
fis = new FileInputStream(keyFile);

// read the bytes
byte value[] = new byte[fis.available()];
fis.read(value);
bais = new ByteArrayInputStream(value);

// get X509 certificate factory
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");

// certificate factory can now create the certificate 
return (X509Certificate)certFactory.generateCertificate(bais);
}
finally 
{
IOUtils.closeQuietly(fis);
IOUtils.closeQuietly(bais);
}
}

The parameter you pass in should be the absolute location of the certificate file. In my case, my String parameter is something like "C:\\cert\AppleWWDRCA.cer".

Thursday 6 September 2012

Programmatically get User-Agent String - Android and iOS

This post details how to obtain a device's default User-Agent string for Android and iOS development.

Getting programmatic access to a device's User-Agent string isn't always easy; in fact, it seems outright difficult in a lot of cases. This post will detail how you should obtain such information when developing for Android and iOS. Note, the information on the Android section requires Android 2.1 and up (more details below).

What is a User-Agent string?

User-Agent strings provide details about the device that is making a request to a server. Basically, it is a way of helping the server identify what kind of device is asking for data.

Here is an example, of an iPhone running iOS 6 (Beta):
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.2

What does 'default User-Agent' really mean?

That's a good question. By my definition, the default User-Agent basically refers to the User-Agent string that the device's default browser would use when requesting websites. Each browser of course, will likely send a modified User-Agent string.

Why do I need to care?

User-Agent strings are often an essential piece of information when submitting data to a server; the server might require it to cater device-specific optimised data, the server might just want to log it, or it might well just refuse to release data to a request that hasn't provided it.

In the simplest of examples, when using a browser to access a website, the server might look at your User-Agent string and decide whether to serve you the "desktop" page, or the "mobile" page, based on what data is provided in your User-Agent string.

Providing a hardcoded User-Agent string in your app might seem tempting, but this can really result in a bad user experience for users, depending on how/if the server uses that information. As every good developer knows; it is all about the user experience! Thankfully, you don't have to bend over backwards to find the User-Agent string.

Android

Since Android 2.1, a system property has been included which contains the default User-Agent string. This is really great as it allows quick retrieval of the UA string without the overhead of creating a Web View, simply to extract the string.

String userAgent = System.getProperty("http.agent");

iOS

In order to obtain the User-Agent on iOS, you need to instantiate a UIWebView, and execute a javascript command to obtain the UA string.

UIWebView *webView = [[UIWebView alloc]initWithFrame:CGRectZero]; 
NSString *uaString = [webView stringByEvaluatingJavaScriptFromString:"@navigator.userAgent"];


A good tip here is to execute the above once, and assign it to a variable - the User-Agent string is not going to change during the execution of your app, and it is more efficient than performing an instantiation of a UIWebView often, just to get the same string.

Wednesday 22 August 2012

Android Jelly Bean - Stop Individual Apps From Showing Notifications

The ability to disable notifications on a per-app basis was introduced in Android 4.1, Jelly Bean. It is simple to do, but easy to miss as a feature.

For apps that provide configuration to adjust the frequency of notifications, or disable them altogether, you should go ahead and configure them in-app. These instructions are more for those pesky apps, that abuse your notification bar to promote other apps, or give you unwanted notifications with no way to disable them. Quite a few apps must use this method to subsidise the distribution of the app without a price tag. I have no sympathy for these app makers however; this is a clear abuse of the notification system and as a person who can't bear to have an unread email, missed call or indeed notification, I'd sooner uninstall the app than have this kind of mental torment applied to me.

Since Jelly Bean however, I don't need to uninstall the app any more. I can simply disable that app's right to use the notification bar.

With a Notification Present

The easiest time to do this, is when presented with a notification from your notification bar that you wish would stop. From the notification bar, simply press and hold on the notification, and choose "App Info".

Abuse of the notification bar

No Notification Currently Present

Alternatively, you can access the same menu, but going to "Settings", "Apps" and tapping the offending app. Of course, this method requires you to know which app is causing the notification, which might not always be easy if the app is crafty. If required, just wait until the next time the notifications appears, and follow the step detailed above.

App Info Screen

The app info screen shows various data about the app, and provides options to uninstall, force stop and clear any data the app has saved. Additionally, though, and crucial to the purpose of this post, there is a checkbox labelled "Show notifications". 

N64 Emulator is to blame


Unchecking this will result in a prompt warning you that you might miss out on pesky ads, pushed to your face when you are busy doing something else (paraphrased somewhat).



You are now free of that app's right to notify. Kick your feet up, sit back and wait for the developer to provide an update to their app which finds another loophole to exploit.



Tuesday 21 August 2012

iOS 6 User Agent String

Today, I needed to have a look at the User Agent string from a device running iOS 6. A device I have is running iOS 6 beta 4, provides the following header.

User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 

As this is a beta, there is the chance it will change, and of course, it might even vary from iPhone to iPhone. However, for anyone needing an example User Agent string, there you go.

If you are looking for gaining access to a device's User-Agent string programmatically, then you should look no further than here, http://cdrussell.blogspot.co.uk/2012/09/programmatically-get-user-agent-string.html.

Tuesday 31 July 2012

Could Not Instantiate Class Named 'NSLayoutConstraint'

Back into iPhone development again, I found myself briefly scratching my head at an app crashing when testing on one of my real devices, but not another. The console shows a stack trace with the pertinent complaint being


*** Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: 'Could not instantiate class named NSLayoutConstraint'*** 

Well that's no good now is it ? I found that it runs just fine on my iPhone running iOS 6 beta 3, but crashes out with the above error when running on my iPod 5.1. It turns out that the new Xcode has introduced some new functionality into the Interface Builder, some of which is enabled by default when creating new XIBs.

The offending item is this case, is the use of "Autolayout" of interfaces. It seems that when using Xcode to create new interfaces, Autolayout is checked by default, even though it is a feature available for iOS 6 onwards. My project targets iOS 4, so I was a bit surprised to see it assume such a breaking setting, but maybe that is just because the Xcode version I am using is also still in beta (Version 4.5 4g125j).

The fix is quite simple, Select the XIB which is failing to load, and from the panes down the right, find the one entitled "Interface Builder Document". Under here, are settings for deployment versions (which represents the minimum version you want to support), which can be changed to match your expectations.





Selecting the correct version will result in the build failing, quoting a build error such that:

Auto Layout on iOS Versions prior to 6.0

Underneath this setting, is a checkbox entitled "Use Autolayout" which should be unchecked, as per the build error. Rinse and repeat for any other XIBs you have created using Xcode 4.5.

Tuesday 17 July 2012

BBC News App for Asus Nexus 7 Tablet

** Update #1 - 19th July, 2012 **
I notice that the BBC news app is now showing up in the Play store, however, it is not optimised for the tablet. In fact, it is the mobile version of the application which is perfectly functional but not as nice of a user experience. I've opted to stick with tablet optimised version for now, which can be downloaded below. Feel free to try both out, one will simply install over the other. The official version is available here.
**

You will find the BBC News app link below. Nexus 7 can run the BBC News app, however a search of the UK Google Play store will reveal that the app is incompatible with the Nexus 7. I'm sure the BBC will update the app's compatibility shortly, rendering this post pointless when it does, but in the meantime this post will provide details on how to get the BBC News app on your shiny new Nexus 7. Note, this is the tablet version of the app, so is optimised for tablet layouts.

You will need to allow downloads from 3rd party locations. That is enter Settings, Security, then check the option to allow installs from Unknown Sources.

Download the APK from here. Open the APK and accept the install.

As you can see, it looks pretty good. Videos can be played using a 3rd party app, such as MX Player.

screenshot showing the app in landscape mode
in landscape  mode

Thursday 14 June 2012

Create JSON manifest file for directory

The following bash shell script was created for a very specific purpose; however due to licensing agreements the exact purpose cannot be disclosed as of yet. However, the techniques used here might prove useful to others striving to solve similar problems to that which I have just solved.

This script will inspect a directory of your choosing, and for each file in the directory, calculate the SHA-1 hash. A JSON file will then be created called "manifest.json" which lists each file along with its corresponding hash.

Usage: (Created and tested on a Mac)
From a Terminal prompt, enter:
bash manifestMaker.sh dir
where dir is the directory for which you want to create a manifest.

Script
Download the script from ManifestMaker, or see below for the source code. Remember to make the script executable.

#!/bin/bash

#script which generates a JSON file, which has as keys the filenames of all files in the given directory, and as values the SHA1 digest of the corresponding files

#check number of arguments is as expected
if [ $# -ne 1 ]
then
    echo "Need to provide directory on which to operate"
    exit 1
fi

src="$1"

#check directory exists
if [ ! -d $src ]
then
    echo "Directory $src does not exist"
    exit 2
fi

#change to specified directory
cd $src

#declare target output file
out="manifest.json"

#clean up first
rm $out
touch $out

#find the number of files - needed to know whether to put a "," at the end of each line or not
numFiles=$(ls | wc -l)

#start printing to the file
echo "{" >> $out

index=1
for file in *
do
#check if comma is needed at the end of the line
if [ "$index" -lt "$numFiles" ]
then
endLine=","
else
endLine=""
fi

#gives some unfriendly output formatting
digestFull=$(openssl sha1 $file)

#tidy up formatting to get actual value
digest=${digestFull#*= }

#print line to file
echo "    \"$file\" : \"$digest\"$endLine" >> $out

#increment our index
index=$[$index+1]

done

echo "}" >> $out

Thursday 12 April 2012

iOS - Integrating KIF Tests with Jenkins CI


Below is the script I use in Jenkins to kick off my automated GUI tests, which are performed using KIF which was developed by Square. KIF serves as an excellent start for running and automating GUI testing. I had to make a small change to KIF to support entering text into text fields which weren't empty; KIF didn't seem to handle this so I made a fix in a fork, located at my KIF.


One of the problems I had initially was that whilst the build would be marked as successful or a failure correctly, there was no feedback on which of the GUI tests failed. My script below solves this problem by outputting the feedback from KIF if and only if the GUI tests fail; if they are successful there is no need to see them in my opinion.


Your script would need to be adapted to contain the correct details for your project/workspace/scheme/target of course, but hopefully this will serve as a good guide.


Waxsim is the required bridging application needed to support integration between KIF and Jenkins. Waxsim has been installed on the server, running under /Applications/waxsim - if you have it installed elsewhere then update the reference accordingly.


#!/bin/sh
#kill simulator if running
killall -s "iPhone Simulator" &> /dev/null
if [ $? -eq 0 ]; then
    killall -KILL -m "iPhone Simulator"
fi


echo "About to build GUI tests scheme"


xcodebuild -scheme "GUITests" -workspace "xxx.xcworkspace" -configuration Debug -sdk "/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/" build CONFIGURATION_BUILD_DIR="/var/hudson/workspace/$JOB_NAME/build" 


echo "About to run waxsim - a long delay here might suggest something is obstructing the view of the simulator on build slave"


/Applications/waxsim "/var/hudson/workspace/$JOB_NAME/build/GUITests.app" > /tmp/KIF-$$.out 2>&1


# WaxSim hides the return value from the app, so to determine success we search for a "no failures" line


echo "Testing finished"


# count the number of times "TESTING FINISHED: 0 failures" is found - 0 means that there was a failure
success=`exec grep -c "TESTING FINISHED: 0 failures" /tmp/KIF-$$.out`


# if there was a failure, show what waxsim was hiding and crucially return with a non-zero exit code
if [ "$success" = '0' ]
then 
    cat /tmp/KIF-$$.out
    echo "==========================================="
    echo "GUI Tests failed"
    echo "==========================================="
    exit 1
else
    echo "==========================================="
    echo "GUI Tests passed"
    echo "==========================================="
fi

Tuesday 10 April 2012

Script to Kill iPhone Simulator if Running

Instructions to end the iPhone Simulator process if it is running.

As part of a CI setup, it is typical to have multiple iOS build jobs running on an individual build slave. As you can only have a single instance of the iPhone Simulator, you might find some of your jobs fail because another job has started the simulator, and you can't get access to it.

What seems like a sensible approach here, is to kill the simulator if it is already running, meaning your build job is guaranteed to start from a known state. If you attempt to close the simulator when it isn't running, however, you will end up with an error, along the lines of "No matching processes belonging to you were found". That's why you need to check if the process is running first, before attempting to close it.

The following script checks if the simulator is running, and if it is, ends the process. You can do this at the start of each build job.


#!/bin/sh
#kill simulator if running
killall -s "iPhone Simulator" &> /dev/null
if [ $? -eq 0 ]; then
    killall -m -KILL "iPhone Simulator"
fi




And in case you were wondering; this works for the iPad simulator too. The process is the same so the script above works unedited.


If you have any problems getting this going, drop me a comment below.

Thursday 9 February 2012

Rotating BlackBerry Playbook Simulator

I'll keep this one short and snappy.

If you want to change from the default 'landscape' mode, click and hold the mouse from the bottom right hand edge of the simulator (just anywhere in the black section will do) and drag towards the centre of the screen, and release.

Saturday 7 January 2012

Could Not Reconnect All Network Drives in Windows 7

I often see the notification balloon popping up from system tray to tell me that my network drives could not reconnect. I typically see this after I've first turned on my computer or after I've booted from Hibernation.



When I open Computer, I can see my network drives, but they are shown with a red cross indicating that there is indeed a problem with the connection to them. Applications which rely on data from these network drives cannot obtain their data, and fail in their own ways. The particularly annoying aspect of this is that there absolutely no problem with the network connection to these drives. Simply double clicking on the drives will open them up and restore the connection. All applications will function correctly thereafter... until the next time I boot up the machine.

Below is a workaround you can use to ensure your remote drives are available for use when you turn on your PC. For my environment, I'm running Windows 7 64-bit and have a Synology DS411J which is a NAS drive. But really, this should work for connecting any remote network drive.