Category Archives: Android

Changing Target Android Version in Eclipse

12 Mar 2012

This one takes care of the “Attribute minSdkVersion(x) is lower that the project target API level(x)” error that you may encounter if attmpeting to change your app’s Android target version after having setup your Eclipse project for Phonegap. The error manifests itself as a build error with this the following description (which illustrates a target of 8 being below the projects API level of 10):

To change the target Android API level you need to do two things:

  1. Update the Android Manifest
  2. Update the App Target API Level

See this page for more information on Build Targets and
acceptable values: Android API Level Guide.

First go to your project folder in Eclipse. In the root you should find a file called “AndroidManifest.xml“. Double-click this file. Eclipse will either show you the UI view of the XML document, which looks like this:

Or Eclipse may show you the manifest in source view which would look like this:

In the UI view of the manifest you are looking for the “Min SDK version” filed – set that to the desired SDK version that you want to target. Save the change and then close the manifest. If you cannot locate the Min SDK Version then look under “Manifest Extras” and click on “Uses SDK” – the desired field will appear to the right under the “Attributes for Uses SDK” heading.

If you see the source-code view for the manifest then look for this line:

...

...

… and simply change the number (“8” in the above example) to whichever target API Level that you’re going for.

Next, you need to change the Project Build Target. To do so you will locate the root of your project within the Project Explorer (this appears as a directory tree structure), right click the app project root and select “Properties“. You will see the properties panel which looks like this:

At this point simply select the proper build target, apply and close.

XTemplates and Sheets in Sencha Touch

24 Feb 2012

I was recently working on a project where there was specific help text that proved to be quite long in many cases. The original plan was to use Ext.Msg but that quickly became impractical due to the sheer amount of text. Next thing to use was Ext.Sheet which has the flexibility and options that I needed.

The Sheet ended up configured to have a toolbar and footer, docked to the top and bottom respectively. I further configured it to float in the center of the screen with about a 40 pixel margin around the sides. The lone item in the Sheet was a panel configured to use an XTemplate so that I could update it as needed with whatever help text came across the help web service.

The problem I encountered was that I could only populate the content panel within the Sheet once. It turns out that the activate listener for the content panel only ever fires one time – the very first time that the Sheet is displayed which is the first time the panel is activated.

After some thought this behavior makes sense. The panel is rendered the first time and the activate event fires. Thereafter, it doesn’t need to fire because it is always the lone active item of the sheet – it has already been activated/rendered. If I were to add another item to the sheet and switch between the two I would then get the activate event firing for both panels every time they are switched. As it is, the panel is activated the very first time out of necessity, activate naturally fires, and that is pretty much the end of it.

Back to the task at hand, I don’t have anything bound to stores, no proxies… I didn’t want to mess with that, I just wanted to push an object into the panel and I could only do it once with the panel’s activate event. Every other call to show the Sheet would not update the content with new text – only the very first item was ever displayed and all further use of the Sheet would only show the content that it was initially updated with.

See the following code which fits this scenario:

...
var helpSheet_tpl = new Ext.XTemplate(
    '',
        '
{data}
', '
', { compiled:true } ); var helpContent = new Ext.Panel({ id:'helpContent', layout:'card', scroll:'vertical', tpl:helpSheet_tpl, listeners:{ activate:function(e,o){ // this event will only ever fire once!!! helpContent.update(DATA); } } }); var helpSheet = new Ext.Sheet({ id:'helpSheet', cardSwitchAnimation:{type:'pop',duration:1000}, height: window.innerHeight - 40, width: window.innerWidth - 40, centered:true, floating:'true', layout: { type: 'card', align: 'stretch' }, items:[ helpContent ], dockedItems:[ { xtype:'toolbar', layout:'card', dock:'top', id:'help_header', title:'Help', }, { xtype:'button',text:'OK', layout:'card', dock:'bottom', listeners:{ tap:function(o,e){ helpSheet.hide(); } } } ] }); ...

The learning here is that the activate event for the above content panel will only fire once because it is the lone item – there is no item-switching happening and so therefore no opportunities for activate to fire again. Also, Ext.Sheet doesn’t have any events so you can’t put an activate event listener on it. At least, when I attempted this nothing happened at all. The Sencha docs imply that it is inherited but if you check the option to hide inherited you will see that there is nothing left event-wise for Ext.Sheet.

If you want to push an object into a panel in the above scenario by using the panel update() method you will have to do so outside of the panel, and not rely on the panel’s activate event. In my case I placed it within the JSONP callback. First I use the show() method to show the sheet and then I follow that up with the update() method on the panel.

Making a Phone Call from Within PhoneGap in Android and iOS

22 Feb 2012

Today I came across a feature request that I had not done before – dialing a number from within an app. Some quick research shows that its possible using a specific URI scheme.

What are URI schemes? Honestly Wikipedia does a better job than I ever could in describing them but I think of them as something that allows a specific piece of functionality to happen over the internet, and thus they are usually referred to as protocols. You probably have already seen them – the most common ones are http: and https: (for web browsing), and ftp:, among others. Some are unique to an application and really don’t qualify as schemes and are definitely not a “protocol”, such as mailto: (to open up the mail client on a person’s computer), javascript: or about: – in fact, try typing about: in the address bar of your browser and hit “enter” on your keyboard, notice what happens…

In our case where we want to dial a number from within our app we need a way of telling the mobile phone that we want to make a call. There is a scheme for this purpose called tel:. A sample number using this scheme would look like this: “tel:+1-800-555-1234”. If you wanted a number to work around the world you would use an international number which includes the country code.

Implementing this is simple, we could do this within our mobile html5 app like so:

...
call this number
...

Ideally though we would delegate the event and fire a function to call our mythical phone number. To send the url (the “tel” url) to the browser we would write the following:

...
document.location.href = 'tel:+1-800-555-1234';
...

As of PhoneGap 3.6 all schemes are subject to whitelists. This means you have to add the tel scheme to a second whitelist that will allow your app to launch external applications. To do this you need to edit your config.XML to include the following (a mailto example is included):



Go here for more information: Cordova 3.6.0 Whitelist Guide.

Of interest to this topic is getting Android to treat phone numbers (as well as URLs and mailto schemes) as clickable links in text fields. I’ve not tested it but try adding the following to your config.xml.


Additional information on this can be found here: http://developer.android.com/reference/android/widget/TextView.html#attr_android:autoLink.

[EDIT: Note that what follows no longer applies but remains here for historical purposes.]

When we run the above code in Android 2.3.6 the phone dialer appears and does so with our number pre-populated ready to be dialed. Unfortunately on iOS 5 this doesn’t happen. A quick review of iOS documentation implies that it should work – so I suppose its just broken.
No need to panic, there is a PhoneGap plugin available which will take care of things. The plugin can be downloaded from here:

Click here to download the iOS Phone Dialer PhoneGap plugin

Its simple to install – just drag and drop the “m” and “h” files on to the classes folder of your xcode project. When you do this a dialog will appear with some options – be sure to click the radio button for copying “…files if needed..”.

Next, update the PhoneGap.plist file to reflect that you are adding a new plugin. The link for downloading the plugin explains the plist values as being “phonedialer > PhoneDialer”… but I think its easier to explain with an image:

The final step is to place the “PhoneDialer.js” javascript file somewhere within the root of your project and then to add it to your index.html file via a script tag.

Now that the Phone Dialer plugin is installed you’ll naturally want to know how to use it:

...
window.plugins.phoneDialer.dial('1-800-555-1234');
...

All in all pretty easy and straight forward, however now you have two methods of dialing a number within a single project. What you want is to use the tel: url scheme in Android and the Phone Dialer plugin in iOS.

Within Sencha Touch we have something called the Ext.is object whose attributes reflect everything that you could possibly want to know about the environment that your mobile app is living within.

For our purposes all we want to know is if we are in iOS or if we are in Android. These two lines provide us the answer:

...
Ext.is.Android // boolean, "true" for android, false otherwise
Ext.is.iOS // boolean, "true" for iOS, false otherwise
...

Thats all we need to impliment phone dialing across the two platforms within our mobile app. Lets build a function that makes use of one of the above (we don’t need both) and we should also give the user a choice in the matter, so the code below includes a message to the user to see if they really do want to suspend the app in favor of the device’s phone dialer:

...
function callSomeone(){
    var msg = Ext.Msg.confirm('Please Confirm','Are you sure you want to make a phone call?',
        function(r){
            if (r == 'yes'){
                if (Ext.is.Android){
                    document.location.href = 'tel:+1-800-555-1234';
		} else { // we assume the device is running iOS
		    window.plugins.phoneDialer.dial('1-800-555-1234');
		}
	    }   
	});
    msg.doComponentLayout();
}
...

All done… I suppose the very last thing to do here is to provide a complete working Sencha Touch example, and some screen captures…

...
Ext.setup({
    onReady: function(){
	
        var rootPanel = new Ext.form.FormPanel({
	    fullscreen: true,
	        items: [
		    {
			xtype:'button',
			text:'Call 1-800-555-1234',
			scope:this,
			handler: callSomeone
		    }
		],
		dockedItems:[
		    {
			xtype:'toolbar',
			dock:'top',
			title:'Phone Dialer Example'
		    }          
		]
	    }
	);
	
	function callSomeone(){
	    var msg = Ext.Msg.confirm('Please Confirm','Are you sure you want to make a phone call?',
		function(r){
		    if (r == 'yes'){
		        if (Ext.is.Android){
		            document.location.href = 'tel:+1-800-555-1234';
			} else { // we assume the device is running iOS
			    window.plugins.phoneDialer.dial('1-800-555-1234');
			}
		    }   
		}
	    );
	    msg.doComponentLayout();
	}

    }
});
...

The final product on a Samsung Galaxy S2:

Set App Scaling in PhoneGap, Android

10 Feb 2012

One of the very first things any Android PhoneGap developer will run into is setting the appview scale to set the proper view size and prevent “zooming” within your app. The “zoom” could happen if the user taps a text field or pinches the screen. We want the scale set appropriately and zooming disabled.

Open up your main activity.java (located in your “src” directory) and compare it with what I have below:

package com.rickluna.myapp;

import com.phonegap.*;
import android.os.Bundle;
import android.view.WindowManager;
import android.webkit.WebSettings;

public class myAppActivity extends DroidGap {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.loadUrl("file:///android_asset/www/index.html");

        /* lock scaling in android 2.2 code to be pasted right here */

    }
}

The fix for this is easy and tested on Android 2.2, Android 2.3.3, and Android 3.1:

...
        /* lock scaling in android 2.2 */
        appView.getSettings().setSupportZoom(false);
        appView.getSettings().setUseWideViewPort(false);
        WebSettings ws = appView.getSettings();
        ws.setDefaultZoom(WebSettings.ZoomDensity.MEDIUM);
        appView.getSettings().setDefaultZoom(WebSettings.ZoomDensity.MEDIUM);
        appView.setInitialScale(0);
        ws.setSupportZoom(false);
        ws.setBuiltInZoomControls(false);
        ws.setUseWideViewPort(false);
...

The thing that you may find good to know is that you can specify the zoom density to whatever you like. I find that MEDIUM and initialScale of “0” gives me something that looks great across Android phones and looks consistent with what happens on iPhones. That being said I have used FAR on one occasion with initialScale set to “100”.

According to the android docs FAR should have an initial scale of “100” and MEDIUM should be “150”, however, according to my testing, you want an initialScale of 0 for MEDIUM. The reason is that in Android 2.3.6 (which many current phones are running such as the popular Samsung Galaxy S2)  the webview zooms in when tapping a text field. To prevent that and get consistent behavior across the range of android versions I have to set the intialScale to “0”, so the final setting is initialscale 0 and ZoomDensity MEDIUM.