Turning off iOS wordwrap in Titanium

Titanium Mobile 2.1 allows you to display read-only text using the Label class. This has a property called wordWrap that, when you set it to false, will stop the text from wrapping to a line below it. Pretty crucial for a screen with limited space you would have thought. The only problem is that it doesn’t seem to work with iPad/iPhone as I found when trying to port an app I’d initially developed for Android.

The only workaround I can find at present is to use the minimumFontSize property. If you use this iOS will automatically scale the font size downwards until the text fits the width of the label or it reaches your specified minimum size. If it can’t fit it in then instead of wrapping it chops it off with ellipses. This looks rubbish though, and for some reason shifts the whole text downwards by a few pixels too. However if you set the minimum font size to the same as the required font and also set the height to something only slightly larger than the required font size then it will behave itself. Note setting height to Ti.UI.SIZE does not work.

I couldn’t find anything obvious on the Web about this (other than people moaning about it), so I thought I’d post how I’d got around it.

  var rel = Titanium.UI.createLabel({
        text : "some very long text that won't fit in the label on one line",
        font : {
            fontSize : 16,
            fontWeight : 'bold'
        },
        minimumFontSize: 16,  // do this for word wrap for iPad/iPhone ...
        height : 20,          // ... and you need to hardcode a height too!
        width: Ti.UI.FILL,    

        wordWrap : false      // for Android
       
    });

How to get Titanium to recognise Samsung Galaxy Tab 2 as an Android device

When I first plugged my new Galaxy Tab 2 into the USB port a notification message popped up on my Windows 7 PC (64 bit) to say that the drivers had been automatically installed. Brilliant. That meant I could just expect Titanium to recognise it as a device and install my app on it right? Wrong. The Android SDK just wasn’t recognising it at all.

Note: To discover the devices that the SDK does recognise (if any), go to the command prompt, change directory to C:\Program Files (x86)\Android\android-sdk\platform-tools and then type adb devices.

If you Google then you will get some of the way there: you will find that on your Android device you need to go to Settings->Developer Options and then check the USB Debugging and Allow Mock Locations boxes. I did it. I reconnected the USB. Rebooted both the device and PC. Still no joy. Then commenced lots of randomish pressing of buttons until I figured it out. With the USB connection plugged in to the PC you need to go to the USB icon in the Galaxy Tab’s notification bar (next to the clock). Press it and some options will pop up. Press on the ‘Connected as media device’ menu option. You will see that you can connect as a Camera (PTP) instead. Click this. Unconnect the USB cable. Reconnect. Some more drivers will be installed. The MTP driver will fail (well it did on mine anyway) but this doesn’t seem to matter. Check the devices again and you should see a new one listed!

Now if you ‘Install on Android Device’ in Titanium it should automatically pick it up. Note that Titanium only likes having one device connected at a time too.

AutoIt macro for automating Titanium splash screen generation using Paint.NET

A Titanium mobile application has many different splashscreens which are shown when the app starts up. Most of them are for Android apps, which can have a variety of different screen sizes.

I use Paint.NET to create/edit graphics which is great (and free!), but after I did my first screen I could see that it was going to be very tedious to have to go in half a dozen times, resizing and changing the directory each time. This is what macros are for but unfortunately Paint.NET does not have any built in ones. So I found AutoIt, which is a great little tool. Wish I’d found it earlier!

Here is my script for generating landscape splashscreens from a master copy. Note that the dimensions are taken from here, I have not tested with every type of device. Sorry, no code highlighting here, WordPress doesn’t do AutoIt! Portrait script is similar except that you need a couple more for the iPhone too.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; AutoIt macro to create all the landscape splash screens for a Titanium 2.1 mobile app.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$pictureDir = "C:\Users\Fred\Pictures\"  ; Picture root directory - where your original file should be
$outputRootDir = $pictureDir & "AppName\" ; The name of the directory to put your output files (relative to pictureDir) 
$fileName = "NWFBsplash.png"  ; File name of the big iPad image in pictureDir.

$iphoneDir = $outputRootDir & "iphone\" ; iPhone directory
$androidDir = $outputRootDir & "android\" ; android directory
$androidRLLHDir = $androidDir & "res-long-land-hdpi\"
$androidRLLLDir = $androidDir & "res-long-land-ldpi\"
$androidRNLHDir = $androidDir & "res-notlong-land-hdpi\"
$androidRNLMDir = $androidDir & "res-notlong-land-mdpi\"
$androidRNLLDir = $androidDir & "res-notlong-land-ldpi\"

$titleSuffix = " (100%) - Paint.NET v3.5.10" ; Might need to double check your window title here! 100% means fullscreen

$paintDotNetWindowTitle = $fileName & $titleSuffix

; Create output directory if necessary
If DirGetSize($outputRootDir) = -1 Then
   DirCreate($outputRootDir)
Endif 
   ; Create iPhone output directory
if DirGetSize($iphoneDir) = -1 then
   DirCreate($iphoneDir)
EndIf
   
; Create android output directories
if DirGetSize($androidDir) = -1 then
	  DirCreate($androidDir)
EndIf
	  
	  if DirGetSize($androidRLLHDir) = -1 then
		 DirCreate($androidRLLHDir)
	  EndIf
	  
	  if DirGetSize($androidRLLLDir) = -1 then
		 DirCreate($androidRLLLDir)
	  EndIf
	  
	  if DirGetSize($androidRNLHDir) = -1 then
		 DirCreate($androidRNLHDir)
	  EndIf
	  
	  if DirGetSize($androidRNLLDir) = -1 then
		 DirCreate($androidRNLLDir)
	  EndIf
	  
	   if DirGetSize($androidRNLMDir) = -1 then
		 DirCreate($androidRNLMDir)
	  EndIf
	  
  
   
   


; Set working directory to pictureDir
FileChangeDir ( $pictureDir )

; Load up the image into Paint.NET and wait for it to load.
Run("C:\Program Files\Paint.NET\PaintDotNet.exe " & $fileName, $pictureDir)
WinWaitActive($paintDotNetWindowTitle)

; Find the 'Maintain aspect ratio' flag on the resize screen.
$aspectRatioMaintain = RegRead("HKEY_CURRENT_USER\Software\Paint.NET", "LastMaintainAspectRatio")



;;;;;;;;;;;;;;;;;;  iPad - note that iPhone has no landscape splash screen

PopUpResize()

; If the maintain aspect ratio flag has been checked then uncheck it.
; Then send the new width and height and click OK (using ENTER).
if $aspectRatioMaintain == "True" Then
   Send("!m{TAB}1024{TAB}748{ENTER}")
   $aspectRatioMaintain = "False"
Else
   Send("1024{TAB}748{ENTER}")
EndIf

DoSaveAs($iphoneDir, "Default-Landscape.png")


;;;;;;;;;;;;;;;;;; Android high-def long

PopUpResize()
Send("800{TAB}480{ENTER}")
DoSaveAs($androidRLLHDir, "Default.png")


;;;;;;;;;;;;;;;;;; Android high-def notlong

PopUpResize()
Send("800{TAB}480{ENTER}")
DoSaveAs($androidRNLHDir, "Default.png")

;;;;;;;;;;;;;;;;;; Android med-def notlong

PopUpResize()
Send("480{TAB}320{ENTER}")
DoSaveAs($androidRNLMDir, "Default.png")

;;;;;;;;;;;;;;;;;; Android low-def long

PopUpResize()
Send("400{TAB}240{ENTER}")
DoSaveAs($androidRLLLDir, "Default.png")

;;;;;;;;;;;;;;;;;; Android low-def notlong

PopUpResize()
Send("320{TAB}240{ENTER}")
DoSaveAs($androidRNLLDir, "Default.png")

; Close Paint.Net
WinWaitActive($paintDotNetWindowTitle)
WinClose($paintDotNetWindowTitle)
; End of Script

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Func PopUpResize()
   WinWaitActive($paintDotNetWindowTitle)
   ; Send the resize command and wait for the resize window to open.
   Send("^r")
   WinWaitActive("Resize")
EndFunc

;;;;;;;;;;;;;;;;;;
Func DoSaveAs($dir, $file)
   WinWaitActive($paintDotNetWindowTitle)
   ; Send the Save As command and wait for the Save window to open.
   Send("^+s")
   WinWaitActive("Save As")

   ; Send the name of the file
   Send($dir & $file & "{ENTER}")

   ; If prompted that the file already exists then choose Y to overwrite.
   WinWaitActive("Confirm Save As", "", 2);
   Send("!y")

   WinWaitActive("Save Configuration");
   Send("{ENTER}")
   $paintDotNetWindowTitle = $file & $titleSuffix
EndFunc

Closing all windows in a tab with Titanium

I am currently teaching myself Titanium for Android mobile development and one of the problems I immediately hit on was how to close all currently open windows in a tab. With my test application I am trying to write an app with a tab group that will navigate a ‘family tree’ type database, where each window displays a node and has many links to other windows, and you can traverse them in any order, leaving a trail that you can go back along using the back button. It is of course possible that the same node in the tree may appear more than once in the window hierarchy.

This is great (I think), but what if the user wants to start a new search? It would be tedious in the extreme to go back through all the windows to the beginning. I wanted to be able to just close all the currently open windows and go back to the root tab. My problem was that Titanium does not keep a list of the currently open windows. I could just close the tab group and then recreate it all but that seems a little inelegant. Besides, I was trying to refresh my knowledge of CommonJS and OO with Javascript too. 🙂 I was surprised about how little I could find about this on the web, (possibly it is something I shouldn’t be doing at all, but hey-ho), so I decided to just put together something for self-reference.

What I came up with was a globally held list of unique window IDs, but that is about as un-OO as it gets, the rest is done (hopefully) properly. Each time a new window was created it had its own GUID which was added to the list, and a global custom event handler was added for that window. When the ‘closeAll’ function is called it then fires the event for each window on the list. Possibly the code below will explain it a little better. Sorry for the mish-mash of styles, as I say, I am still learning!

‘app.js’ just sets up the tab group

// AppTabGroup just returns tab group - swiped from here: https://github.com/predominant/TItanium-OData-App-Example
require('lib/require_patch').monkeypatch(this);

(function() {
    "use strict";
    var globs = require('lib/globals'), GlobalEvents=require('lib/GlobalEvents');

    var AppTabGroup = require('ui/AppTabGroup'), FooSearch = require('ui/FooSearch');
    var BarSearch = require('ui/BarSearch');

    //create our global tab group
    globs.GV.tabs = new AppTabGroup({
        title : 'Foo'
        window : new FooSearch({
            title : 'Foo',
            exitOnClose:true
        })
    }, {
        title : 'Bar',
        window : new BarSearch({
            title : 'Bar',
            exitOnClose:true
        })
    });

    // Add a listener for the 'shake' event (code is below). Have to admit I've not tested
    // this bit yet - I don't currently have a device to shake, and the emulator doesn't do it!
    var ge = new GlobalEvents();
    ge.addGlobalListeners();
    globs.GV.tabs.open();
}());

‘globals.js’ just hold global variables for the current tabs and windows

exports.GV = {
    tabs : null,
    openWindows: []
};

‘GlobalEvents.js’ holds the functions for managing the list of windows

var globs;

// Fire the 'closeAllWindows' event for each open window on the list.
function closeCurrentWindows(e){
    "use strict";
    
    var i;
    
    for (i=0; i<globs.GV.openWindows.length; i++){
        // The name of the event to fire has the format 'closeAllWindows:<window_id>'.
        // Also send the actual ID through as part of the event data.
        Titanium.App.fireEvent("closeAllWindows:" + globs.GV.openWindows[i], {winId:globs.GV.openWindows[i]});
    }
    
    globs.GV.openWindows.length = 0;
    
}

// Add the passed ID to the list of windows currently open
function addToWindowList(winId){
    "use strict";
    globs.GV.openWindows.push(winId);
}

// Remove the passed ID from the list of currently open windows 
function removeFromWindowList(winId){
    "use strict";
     var i = globs.GV.openWindows.indexOf(winId);
       if (i !== -1){
           globs.GV.openWindows.splice(i, 1);
       }
}

// Add a listener for the shake event
function addShake() {"use strict";

    Ti.Gesture.addEventListener('shake', closeCurrentWindows);
    var gestureAdded = true;

    if (Titanium.Platform.name === 'android') {
        Ti.Android.currentActivity.addEventListener('pause', function(e) {
            // called when app is put into the background
            if (gestureAdded) {
                Ti.API.info("removing pinch callback on pause");
                Ti.Gesture.removeEventListener('shake', closeCurrentWindows);
            }
        });
        Ti.Android.currentActivity.addEventListener('resume', function(e) {
            if (gestureAdded) {
                Ti.API.info("adding pinch callback on resume");
                Ti.Gesture.addEventListener('shake', closeCurrentWindows);
            }
        });
    }
}


function addGlobalListeners(){
    "use strict";
    addShake();    
}


function GlobalEvents(){
    "use strict";
    globs = require('lib/globals');
    this.addGlobalListeners=addGlobalListeners;
    this.closeCurrentWindows=closeCurrentWindows;
    this.addToWindowList=addToWindowList;
    this.removeFromWindowList=removeFromWindowList;
}

module.exports=GlobalEvents;

I did not want the first search screens to also disappear, so they are not included on the window list, however the child window that they call (which displays a list of possible matches) is one that requires closing. Here is ‘FooSearch.js’

exports.FooSearch = function(args) {
    "use strict";
    var globs = require('lib/globals');
    var FooList = require('ui/FooList');

    var instance = Ti.UI.createWindow(args);

    // add text boxes etc to search window here...
    var btnSubmit = Ti.UI.createButton({
        title : 'Search',
        top : 5
    });

    instance.add(btnSubmit);


    btnSubmit.addEventListener('click', function(e) {
        // PseudoGuid generates the GUID. Listed below.
        var PseudoGuid = require('lib/PseudoGuid');
        var pg = new PseudoGuid();
        globs.GV.tabs.currentTab.open(new FooList({
                title : 'Foo Found',
                searchVal : , //Whatever your search value is!
                winId: pg.generate() // Call the generate ID function (see below)
                
            }));
    });
    return instance;
};

As you can see, the new window in FooList will have a property called winId which contains the result of a GUID generation routine. I guess the routine itself is not that relevant here but I include it for reference:

// The GUID generation mechnanism comes from: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
// Such long GUIDs are probably a bit overkill in my case but I liked it!
function generate(){
    "use strict";
    
    var S4 = function() {
       return ((Math.floor((1+Math.random())*0x10000))).toString(16).substring(1);
    };
    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}

function PseudoGuid(){
    "use strict";
    this.generate=generate;
}

module.exports=PseudoGuid;

Now finally below is ‘FooList.js’, which shows how my app’s windows handled the custom event. All other windows that were called from here are handled in exactly the same way.

function PeopleList(args) {"use strict";
    globs = require('lib/globals');
    var GlobalEvents = require('lib/GlobalEvents');
    var ge = new GlobalEvents();

    var instance = Ti.UI.createWindow(args);
    this.win = instance;
    var self = this;
    
    // The callback function used by the closeAll event handler (below). Remember the
    // context is that of Titanium.App, not FooList, so we need to use a self reference (lookup
    // 'closures' if you are unsure what this is).
    this.closeAllCallback = function(e) {
        if (e.winId === self.win.winId) {
            self.win.fromCloseAll = true;
            self.win.close();
        }
    }; 

    // The window has been opened so add to the list
    instance.addEventListener('open', function(e) {
        ge.addToWindowList(this.winId);
    });
    
    // Look for the window close event - remember this can also be called using the back button
    instance.addEventListener('close', function(e) {
        // Only remove from the window list if called via back button - as the GlobalEvent handler
        // does it when closeAll is called.
        if (e.source.fromCloseAll === null) {
            ge.removeFromWindowList(e.source.winId);
        }
        
        // remove the custom event for this window (apparently Titanium can't do this itself
        Titanium.App.removeEventListener('closeAllWindows:' + instance.winId, self.closeAllCallback);

        // remember to clear up after yourself, as Titanium doesn't!
        this.remove(this.children[0]);
        this.children[0] = null;
        globs = null;
    }); 

    // Only have the emulator and want to test this? Use doubletap - emulator doesn't do 'shake'!
    var globEvents = new GlobalEvents();
    instance.addEventListener('doubletap', ge.closeCurrentWindows);

    // Add the custom even for this window
    Titanium.App.addEventListener('closeAllWindows:' + instance.winId, self.closeAllCallback);
    

    // At this point do any displaying of data, links to new windows etc...

   
    return instance;
}

module.exports = FooList;

I mainly created this post for my own and my team’s reference, but hopefully others will find it useful too. I may update it once I get a better handle on memory management etc, I’m not sure how well this does that at the moment.

Titanium HTTPClient returns immediately

If you want to consume a web service using Titanium then you need to use the Titanium.Network.createHTTPClient() method. By default however this is asynchronous. There is a ‘sync’ parameter to the open() method on the HTTPClient object however this does not work on Android (and apparently is deprecated anyway). This means that if you want to first call one service, then use the results to call another service, then you need to know that you can’t do this:


var showView = function(view){
    var json_data = getAndShowFirstService(view);
            
    getAndShowSecondService(view, json_data);
}

The above code will execute both calls to the service at roughly the same time. I assume that behind the scenes a new thread is being kicked off to make the call to the service, and then return to the main code immediately. You may get lucky (as obviously the first call is kicked off slightly before the second), however there is no guarantee that the first call will finish before the second starts.

One method to get around this problem is to use a callback function. The code starts to looks more complicated very quickly! First here is the above function rewritten correctly, or rather less incorrectly (I’m new to Titanium :-)):


var showView = function(view){

    getFirstServiceData(view, function(view, json_from_first_service){
           // Run when called from getFirstServiceData()
           showFirstServiceData(view, json_from_first_service);  

           getSecondServiceData(view, json_from_first_service, function(view, json_from_first_service, json_from_second_service){
                // Run when called from getSecondServiceData() 
                showSecondServiceData(view, json_from_second_service);
           });
    });
            
}

The last parameter of getFirstServiceData and getSecondServiceData are themselves anonymous functions – in other words all the code within the anonymous function will get executed when it is called from within the function it is being passed to. You obviously don’t need to pass view and json_from_first_service as parameters, you could hold them as globals, however that is not recommended best practice.

The outline of the four functions mentioned above is shown below. The parameter ‘view’ is either a Titanium Window or a View:


var getFirstServiceData = function(view, callback_function){
     
        var xhr = Titanium.Network.createHTTPClient();
        var url = "http://myservice.com/MyService.svc/Things";
        xhr.open("GET", url);
        xhr.setRequestHeader('accept', "application/json");
       
        xhr.onload = function() {
            var json_data = JSON.parse(xhr.responseText).d;
            
            // The anonymous function is called here  
            callback_function(view, json_data);
        }; 

        xhr.send({}); 
}

var getSecondServiceData = function(view, json_from_first, callback_function){
     
        var xhr = Titanium.Network.createHTTPClient();

        var searchId = json_from_first.Id; 

        var url = "http://myservice.com/MyService.svc/MoreThings(" + searchId +")";
        xhr.open("GET", url);
        xhr.setRequestHeader('accept', "application/json");
       
        xhr.onload = function() {
            var json_data = JSON.parse(xhr.responseText).d;
            
            // The anonymous function is called here  
            callback_function(view, json_from_first, json_data);
        }; 

        xhr.send({}); 
}

var showFirstServiceData = function(view, json_data){
    // Use Titanium UI methods to display on 'view'
}

var showSecondServiceData = function(view, json_data){
    // Use Titanium UI methods to display on 'view'
}

You can use this method to chain together lots of queries, but as you can imagine it gets messy quite quickly!

This post applies to Titanium SDK 2.0.1.