Category Archives: Android

Simple Android Back Buttons in Sencha Architect 2 & Phonegap

15 Oct 2013

As you may know Android has a back button – present as a software back button or in older devices as a capacitive button on the device itself. The question is how to hook into it and get your views to change in Sencha Touch. Sure, Sencha walks you through Routes and such, but all I want is something simple, and this technique is just that, simple and easy to understand.

This approach uses the browser’s history object and updates it with a hash comprised of the current panel’s id. As you navigate about your app the hash is updated as desired. When the user taps Android’s back button the history object’s back() method is fired. Hash changes don’t cause a page reload so your app doesn’t reload either. After firing the back() method we wait a few milliseconds and then fire our own function to update the view based on the current hash.

This works great for an app that is comprised of a single container whose children are the panels that you want to view. More complex structures would require that you get into Sencha Touch’s Routing mechanism (and to be honest, you *should* be using routes).

One Level of Navigation within a single container

Lets review a scenario that is conducive to implementing simple back button functionality – an app built with the following structure:

back_history_structure_1

As you can see this is a very simple app – a single container with one level of children.

To begin lets add 2 custom methods to our application. Start Architect, and click on the “launch” node within the Project inspector and paste the following into the code view:

Ext.define('MyApp.appHistory', {
    statics: {
        goBack: function(){
            if (location.hash.length != 0){
                var hash = location.hash.substring(1);
	            Ext.getCmp('initialView').setActiveItem(Ext.getCmp(hash));
            } else {
                MyApp.Utilities.addHashToUrl();                                      
            }
        },
        addHashToUrl: function(){
            var id = Ext.getCmp('initialView').getActiveItem().id;
            var loc = location.href;
            var hash = location.hash.substring(1);
            var root = null;

            if (loc.indexOf('#') != -1){
                root = loc.split('#');
                location.href = root[0] + '#' + id;
            } else if (id != hash){
                location.href = loc + '#' + id;
            }
        }
    }
});


Ext.define('MyApp.MyView', {
    extends: 'Ext.panel.Panel',
    requires: ['MyApp.appHistory'],
    initComponent: function() {
        MyApp.appHistory.goBack();
    }
});

Ext.define('MyApp.MyView', {
    extends: 'Ext.panel.Panel',
    requires: ['MyApp.appHistory'],
    initComponent: function() {
        MyApp.appHistory.addHashToUrl();
    }
});

What we’ve done here is add an “appHistory” object to our “MyApp” app (“MyApp” is the default namespace that Architect gives your app) and exposed two methods:

  • MyApp.appHistory.goBack() – this handles the back functionality for the app.
  • MyApp.appHistory.addHashToUrl() – this updates the location hash.

Finally we need to hook into PhoneGap’s “backbutton” event. We do so by adding an event listener within our index.html. You’ll notice the typical “deviceready” event listener wrapped by the document’s “load” listener which ensures that our code runs only when the DOM has been loaded and the device is ready:

window.addEventListener('load',function(){
	document.addEventListener('deviceready',function(e){
		// setup the back button
		document.addEventListener('backbutton', function(e){
			history.back() // go back in the browser's history
			setTimeout(function(){
				MyApp.appHistory.goBack(); // update the view against the current hash
			},100);
			return false;
		}, false);
	});
});

Looking at the above we can see that when the “backbutton” event fires we go back in the browser history then we wait a short bit of time to ensure that the location has been updated before following with the call to navigate back within the app.

The last thing to do is to update the hash from within your Sencha application. I’ve placed the ” MyApp.Utilities.addHashToUrl();” method call within my controller’s onButtonTap event which is sufficient for this example.

This is a good starting point – you’ll of course need to modify per your specific needs, have fun!

Speeding Up the Android Emulator with HAXM

27 Aug 2013

The typical experience with the Android Emulator on Windows is that it is essentially unusable. But if you throw hardware acceleration into the mix then it becomes possible to get the emulator working quite well – well enough for everyday use.

The cause of the emulator’s lack of performance is due to it being implemented completely in software. However, Intel has a set of hardware accelerator drivers called HAXM for computer’s utilizing it’s chips. The caveat is that your Intel processor has to support Intel’s Virtualization Technology (VT) in order for the drivers to work. See this link – Microsoft® Hardware-Assisted Virtualization Detection Tool – to see if your system supports Intel’s VT.

Install HAXM

You can install HAXM through the Android SDK Manager (Within Eclipse, go to Window > Android SDK Manager and look for “Intel® Hardware Accelerated Execution Manager” under the Extras folder and install it) or via a separate download/install file. The following link describes both: Installation Instructions for Intel® Hardware Accelerated Execution Manager – Microsoft Windows*.

[EDIT 10/23/13] I recently bought a new laptop and proceeded to setup my android dev environment and had gotten to the point of installing HAXM. I went to the SDK Manager and selected HAXM from the bottom of the list and downloaded it. The SDK Manager indicated that HAXM was installed but it in fact was not – it was merely downloaded but not installed. I had to find where the installer was downloaded and install it manually. The file is downloaded to your “extras” folder which is somewhere within your Eclipse installation directory. I navigated to my Eclipse install folder and did a search for “extras” and quickly found the file. Here’s a screen cap:

haxm_install

You can see that the file you want to execute is IntelHaxm.exe.

I was then presented with this message during the HAXM install:

haxm_vt_disabled

When I clicked the OK button the install failed – the Intel VT must be enabled first before you can install HAXM.

While researching how to do this I came across a blog post that said some people’s Windows 8 machines would no longer detect USB devices after enableing VT. The fix that worked for that particular blogger (and others) was to disable fast boot. Not wanting to fall victim to this bug I went ahead and disabled that feature. If you are in Windows 8 and would like to do the same you can do so in Control Panel, under Power Settings, select the option of choosing what the power button does. Disable the “Fast Startup” option.

Anyway so now I have to enable VT which by the sound of it means that I have to boot into my system’s BIOS. You will have to search on how to do this for your particular machine, but for my Lenovo IdeaPad Y410p i just had to hold down the F2 button during bootup. Once I enabled VT in the bios and rebooted I was able to complete the HAXM install.

haxm_install_success

[/EDIT]

Create a HAXM-enabled AVD

Once HAXM is installed you’ll want to create a HAXM-enabled AVD, here’s the walk-through on how to do so.

Start Eclipse and select Window > Android Virtual Device Manager.

haxm_start_avd

The AVD Manager appears.

haxm_avd_3

Click “New” to create a new Virtual Device.

haxm_new_avd

The “New AVD” window is where you setup the AVD and specify that you want to use HAXM. Note that HAXM is only available for Android 4 and higher. If you set your Android Target to anything below then HAXM will not be available to you.

The first thing to do is to give your AVD a name. As this will be a HAXM accelerated AVD I personally like to use “HAXM” as part of the name. In this case I’ll call the project “my_tablet_HAXM” (note that spaces are not permitted thus the underscore characters in the name).

Next click the Device list and choose “10.1” WXGA (Tablet)”. Immediately you will notice that many of the other fields have been filled in for you, as seen below:

haxm_new_avd_tablet

To use HAXM you must change the “CPU/ABI” field to “Intel Atom (x86)” however you will see that the “CPU/ABI” field is greyed out. HAXM is only available on API level 15 (Android 4.0.3) and higher. Ours is currently set for something much lower so lets bump up the “Target” to 17 (Android 4.2). Now you can change the CPU value to x86.

If however you see the text “No system images installed for this target” in the “CPU/ABI” field you need to go to your Android SDK Manager and install the x86 images for the version of android you’re attempting to use. Once you do that then return to this step and you’ll be able to select “Intel Atom (x86)”.

haxm_choosex86_2

In memory options you can go as high as 1024MB of RAM. I’ve left it as is – half of that at 512 – feel free to go to 1024 but don’t exceed that number as you wont see any benefit.

In the “SD Card” section you will want to give your AVD some memory to store whatever it is your app might be doing – the default seems to be 9mb (if you don’t enter a value). I’m going to enter an arbitrary value of 16MB.

Next at the bottom are 2 options – “Snapshot” and “Use Host GPU”.

“Snapshot” retains the state of your application so that programs installed in one session carry over to the next thus behaving just like a real device. Otherwise whatever you install/configure will be cleared when you shut down the emulator.

“Use Host GPU” is the switch that enables hardware acceleration and utilizes the GPU to speed up screen rendering for Android 4.0.3 (API Level 15, revision 3) or higher. We’re using a target level of 17 so we meet that requirement. Note that as of API Level 17 GPU acceleration is experimental so if you encounter some oddness (i.e., screen flickering) its most likely due to your enabling HAXM for your AVD.

I’ve read that both Snapshot and the GPU can conflict in some way – regardless you likely want both. In this case I’ll forgo the use of Snapshot. Your new AVD should look like this:

haxm_new_avd_settings_ready

Click “Ok” and your new AVD is added to the AVD Manager. You now have this AVD available to you whenever you want to test your apps. If you like you can kick the tires to see how it performs by selecting the new AVD from the list and clicking the “Start” button.

haxm_start_new_avd_3

You’ll have some Launch Options appear, go ahead and skip the options by clicking the “Launch” button.

The Emulator starts…

haxm_avd_is_running

From here go ahead and play with things – there’s a marked improvement in performance.

Mobile Device Detection using the UserAgent

26 Jun 2013

It used to be that you could use css media queries on mobile devices due to the limited screen resolutions available. We knew, for instance, that 480 x 320 was the iPhone’s screen resolution so you could do a media query based on those dimensions and deliver a phone layout, or the 1024×768 resolution was a tablet – which is funny because the iPhone 5 is 1136×640 – so, what is your media query going to do then? Even today designers are making assumptions about the break points for their media queries and there continues to be debate on the topic. I wonder why all the fuss when as time moves on the subject only just gets more confusing and debatable as higher resolutions are no longer solely in the realm of tablets. Today the gamut of resolutions varies to such an extent that you cannot assume that resolution x is a phone or resolution y is a tablet.

IMO if you’re trying to figure out which style sheet to apply you need to first know the sort of mobile device you’re dealing with as that gives you a better idea about the relative physical size of the device to begin with.

The way to do this is via the navigator object’s userAgent property. For iOS things are pretty simple – look for the presence of “iOS” and you know you are dealing with an Apple device. Look for “iPhone” and “iPad” and you know if you have an Apple phone or an Apple tablet.

For Android its mostly just as simple. I say mostly because not every manufacturer sticks to Google’s requirement that the word “Mobile” be present in all Android phone-specific UA strings. Even with that caveat this makes it possible for us to discern an Android phone from an Android tablet.

To take things a step further, you’ll also need to know the Android version numbers as you may find it neccessary to code against specific versions of Android – even within the minor versions – in order to make your app work as desired (or degrade as desired, as the case may be).

So then below you will find my take at this – it is a function that creates an object that contains everything that you may want. At the very least it contains everything that I personally find helpful.

Here’s the code:

...
    var _deviceInfo = {
        ios:false,
        android:false,
        iphone:false,
        ipad:false,
        androidTablet:false,
        androidPhone:false,
        isPhone:false,
        isTablet:false,
        isDesktop:false,
        pixeldensity:1,
        androidVersion:{full:null,major:null,minor:null,revision:null}
    };
	
    (function(){
        var ua = navigator.userAgent.toLowerCase();
        var pf = navigator.platform.toLowerCase();
        var iosTest = /mac.+mobile/gi;
        var andTest = /android/gi;
        var iPhoneTest = /iphone/gi;
        var iPadTest = /ipad/gi;
        var mobile = /mobile/gi;
        var desktopWin = /win/;
        var desktopOSX = /mac/;
        var deviceHeight = window.innerHeight;
        var deviceWidth = window.innerWidth;
        var androidUAVer = /[android\s](\d\.\d\.\d){1}/;
		
        if (iosTest.test(navigator.userAgent)){
            _deviceInfo.ios = true;
        }
        if (andTest.test(navigator.userAgent)){
            _deviceInfo.android = true;
        }
        if (iPhoneTest.test(navigator.userAgent)){
            _deviceInfo.iphone = true;
            _deviceInfo.isPhone = true;
        }
        if (iPadTest.test(navigator.userAgent)){
            _deviceInfo.ipad = true;
            _deviceInfo.isTablet = true;
        }
        if (ua.indexOf('android') != -1 && ua.indexOf('mobile') == -1){
            _deviceInfo.androidTablet = true;
            _deviceInfo.isTablet = true;
        }
        if (ua.indexOf('android') != -1 && ua.indexOf('mobile') != -1){
            _deviceInfo.androidPhone = true;
            _deviceInfo.isPhone = true;
        }
        if(desktopWin.test(pf) || desktopOSX.test(pf)){
            _deviceInfo.isDesktop = true;
        }
        if (_deviceInfo.android){
            var andVer = ua.match(androidUAVer)[1];
            var parts = andVer.split('.');
            _deviceInfo.androidVersion.full = andVer;
            _deviceInfo.androidVersion.major = parts[0]; 
            _deviceInfo.androidVersion.minor = parts[1];
            _deviceInfo.androidVersion.revision = parts[2];
        }
        if (window.devicePixelRatio){
            _deviceInfo.pixeldensity = window.devicePixelRatio;
        }
    })();
...

Ideally you drop the above within your app’s namespace, but once you have it incorporated within your project you can use the _deviceInfo object to learn about the device in question. Take a look at the object declaration at the top – its obvious what you can get out of it.

Note that the isPhone and isTablet properties are just generic “phone” and “tablet” flags and work for both iOS and Android. If you want to get specific, then use the platform specific properties where iphone and ipad are obviously those devices and androidTablet and androidPhone are what their titles infer.

An example application of this is writing device-specific style sheets into your Web app (add break-points for the specific device if you’re so inclined):

...
    (function(){
        var _d = your_name_space.getDeviceInfo();
        if (_d.isTablet && _d.ios){
            document.write('');
        }
        if (_d.isTablet && _d.android){
            document.write('');
        }
        if (_d.iphone){
            document.write('');
        }
        if (_d.isPhone && _d.android){
            document.write('');
        }
    })();
...

Together with responsive layouts the above should serve you well.

Another way to make use of this script is for device-specific feature enablement such as in this case where I’m setting application-wide features in jQuery Mobile:

...
    var _d = your_name_space.getDeviceInfo();
    $.extend( $.mobile,{
        defaultDialogTransition: (_d.android ? 'none' : 'pop'),
        defaultPageTransition: (_d.android ? 'none' : 'fade'),
    });
...

Prevent Screen Rotation in PhoneGap / Android

21 Jun 2013

You may have an app at some point that requires a set screen orientation. Using PhoneGap in Android this is done by editing your project’s manifest.xml. This file is located in your project root. Double click the file to open it within Eclipse and note the activity node:

...
<activity android:configChanges="orientation|keyboardHidden"
    android:name=".MyPhoneGapActivity"
    android:label="@string/app_name" > 
...

You will add the following to it:

android:screenOrientation=”portrait”

or

android:screenOrientation=”landscape”

Which would look like this:

...
<activity android:configChanges="orientation|keyboardHidden"
    android:name=".MyPhoneGapActivity"
    android:label="@string/app_name"
    android:screenOrientation="portrait" > 
...

In PhoneGap 2.8.1 they have added a “preference” to config.xml for screen orientation, but oddly it doesn’t seem to work on the Android 4.1.1 test device I have (a Samsung Galaxy Tab 2).

The tag looks like this:

...
<preference name="orientation" value="portrait" />
...

Simply add it above the feature tags in config.xml… but as I mentioned, it doesn’t work. Maybe this is forward looking or maybe its broken in PhoneGap 2.8.1, either way the first method continues to work.

Regular Expression Method Fails on UA Strings

06 Jun 2013

This caught me at first – but upon closer inspection it makes sense – I was writing something that was looking at the User Agent of a device to see if it was Android using the “test” regex method and got the opposite result from what I was expecting. I expected that the “test” regex on the UA for the word “Android” would return true but instead I always got false. Here’s an example:

...
var isAnd = /Android/gi;
console.log(isAnd.test(navigator.userAgent));
...

On the surface this should be true, yet it returns false in the console. See this next example:

...
var isAnd = /Android/gi;
console.log(isAnd.test('android'));
...

Now the above returns true – something in the UA string is breaking the test. Here are the UA’s that I’ve tried with the above:

  • Galaxy Tab 2: Mozilla/5.0 (Linux; U; Android 4.1.1;en-us; GT-P5113 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30
  • Nexus 7: Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19

On a whim I remove the forward slashes and perform the following test:

...
var isAnd = /Android/gi;
console.log(isAnd.test('Mozilla5.0 (Linux; Android 4.1.1; Nexus 7 BuildJRO03D) AppleWebKit535.19 (KHTML, like Gecko) Chrome18.0.1025.166  Safari535.19'));
...

Ah, this is what we’re looking for, that test works. So in order to do a successful pattern match using the “test” regular expression method on the User Agent string you would have to first remove all instances of “/” or escape them with a backslash (\/).

In Regular Expressions certain characters have a special meaning – I would think that a string that the regex is being run on would be immune but that’s apparently not the case. Forward slashes are used to create regular expression literals and if present within the string, well…. who knows what happens, presumeably its interpreted as another regular expression – and then things just won’t work. For that reason forward slashes need to be escaped with another character that has a special meaning in regular expressions – the backslash. So, this “\/” actually represents a single literal forward slash.

Yeah, this gets a little messy, you could do one of two things. You could clean the UA string first by using something like this (which, btw, is a little aggressive but is a good example of what you can do):

...
var str = 'some big string that needs to be "cleaned"';
str.replace(/([\[\]\*\^\&\$\.\(\)\?\/\\\+\{\}\|])/g, "\\$1");
...

Or, and I like this approach a lot for this particular application, you could just use a different method entirely, such as the “search” regex method, which is impervious to this situation:

...
var ua = navigator.userAgent.toLowerCase();
console.log(ua.search('android') != -1)
...

And this gives us the predictable “true” as a result.