Thursday, May 23, 2013

TextField Focus Bug on Titanium Mobile Android

When using the TextField inside a TableView, I noticed that it would lose focus immediately after being selected but the keyboard remains shown. If you type anything on the keyboard, nothing gets displayed on the TextField. After a bit of tinkering, I was able to replicate this behavior with a simple code:
1:       var win1 = Ti.UI.createWindow({  
2:            navBarHidden : true,  
3:            top : 0,  
4:            left : 0,  
5:            right : 0,  
6:            bottom : 0  
7:       });  
8:       var subView = Ti.UI.createView({  
9:            backgroundColor : '#ffffff',  
10:            top : 0,  
11:            left : 0,  
12:            right : 0,  
13:            bottom : 0  
14:       });  
15:       win1.add(subView);  
16:       var _data = [];  
17:       var tableViewRow = Ti.UI.createTableViewRow({  
18:            height : '45dp',  
19:            left : 0,  
20:            right : 0  
21:       });  
22:       var textField = Ti.UI.createTextField({  
23:            top : '5dp',  
24:            left : '20dp',  
25:            right : '20dp',  
26:            height : '35dp'  
27:       });  
28:       tableViewRow.add(textField);  
29:       _data.push(tableViewRow);  
30:       var tableView = Ti.UI.createTableView({  
31:            backgroundColor : '#c0c0c0',  
32:            data : _data,  
33:            top : '10dp',  
34:            left : '20dp',  
35:            right : '20dp',  
36:            bottom : '10dp'  
37:       });  
38:       subView.add(tableView);  
39:       win1.open();  

After searching through the code, I was able to determine that the auto height adjustment is the cause for the problem. To fix the bug, you need to set the height property of the TableView to a fixed value:

1:       var tableView = Ti.UI.createTableView({  
2:            backgroundColor : '#c0c0c0',  
3:            data : _data,  
4:            top : '10dp',  
5:            left : '20dp',  
6:            right : '20dp',  
7:            height : '380dp'  
8:       });  
9:       subView.add(tableView);  

To view the complete source check here

As an update to this solution, I found out that while setting the height of the TableView fixes the focus bug, it also affects the view in a way such that, some TextFields near the bottom of the table becomes obstructed by the android keyboard whenever they are active:


In the picture above, I selected the row labeled TextField 30 but as you can see, it is not visible since the keyboard is obstructing it.

Since I cannot find a suitable fix with this problem on the TableView, I decided it is time to use the ScrollView instead:

From what I have tested, this fixes the focus bug on the TextField, while also having similar display to the TableView.

Complete source for the second application is here

Friday, May 10, 2013

Loading Images from Application Data Directory Vs. Assets Directory on Titanium Mobile Android

While working on lists with images on an application, I noticed something weird: loading images  that I cached (created a copy on the local application directory) sometimes fails and blank views are instead shown on the screen.

At first I thought that the downloader is causing the memory leak since I don't set it to null after downloading the images. However, after setting it null (and the other objects I use for copying the file), nothing changed and still the views become blank after some use. After a lot of searching and trying different ways to load the images, I found out that when I load images from the assets directory, they do not disappear.

So to verify if my observation is correct, I performed a test. I created two separate windows with the same UI layout and objects, but one window is loading from the assets directory and one would load from the application data directory. Here is the comparison of the two views:



The picture on the left is the list loading images from the application data directory and the one on the right loads images from the assets directory. As you can see, some of the images on the left picture disappear after the list is scrolled several times.

In titanium all files under the resources directory are stored in the assets folder. So if the images you need to display will be used for a long time or is a permanent part of the application, I would suggest that you place it in the assets directory. If you need to display cached images, it would be better if you use the WebView just like what I previously did.

Full source code for my test application can be downloaded from here.

Thursday, May 9, 2013

Customise Basic Push Notifications on Urban Airship Android

For this tutorial, I modified the sample application code inside the Urban Airship package. To be able to follow, you must have the Urban Airship Push Sample application working on your side (have the urbanairship.properties file ready) and have the android-support-v4.jar properly setup (used for building the custom notification for the app).

From the sample app, you will notice that the basic push notification layout looks exactly the same for all your push alerts. It is sufficient if you want to make all your notifications standard but sometimes, you want to make differences for your alerts to help the user have a better idea of the type of message received (for example some notifications can be considered more important than others).

To start, you need to modify the MyApplication class. Previously, the code to customize the layout is this block:

1:  CustomPushNotificationBuilder nb = new CustomPushNotificationBuilder();  
2:  nb.statusBarIconDrawableId = R.drawable.icon_small;//custom status bar icon  
3:  nb.layout = R.layout.notification;  
4:  nb.layoutIconDrawableId = R.drawable.icon;//custom layout icon  
5:  nb.layoutIconId = R.id.icon;  
6:  nb.layoutSubjectId = R.id.subject;  
7:  nb.layoutMessageId = R.id.message;  
8:  PushManager.shared().setNotificationBuilder(nb);  

You can replace the code in that block with:

1:  BasicPushNotificationBuilder nb = new BasicPushNotificationBuilder() {  
2:       @Override  
3:       public Notification buildNotification(String alert,   
4:            Map<String, String> extras)   
5:       {  
6:            return null;  
7:       }  
8:  };  
9:  PushManager.shared().setNotificationBuilder(nb);  

The code above will prevent the push manager from handling the creation of the notification.

Going back to the MyApplication class, since we already have the IntentReceiver class as the handler for the intents from the push manager,

1:  PushManager.shared().setIntentReceiver(IntentReceiver.class);  

we can go to this class and start modifying it to handle the notification creation.

First, add a NotificationManager class instance to handle the sending of our custom notification. On my sample application, I placed it within the onReceive method:

1:      if (notificationManager == null) {  
2:           notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);  

Then, find the block that handles the action for a push receive. On the push sample code, it is in here:

1:  if (action.equals(PushManager.ACTION_PUSH_RECEIVED)) {  
2:  }  

Add the code to create and send the Notification: (for this sample application, I am expecting the extra data value from the push to appear on this form: notif_type,notif_id,title,message and with key notif_data )

1:  if (action.equals(PushManager.ACTION_PUSH_RECEIVED)) {  
2:       String notif_test = intent.getStringExtra("notif_data");  
3:       if (notif_test != null) {  
4:             String[] notif_data = notif_test.split(",");  
5:             Uri ringToneUri = RingtoneManager.getDefaultUri((notif_data[0].equalsIgnoreCase("0")) ? RingtoneManager.TYPE_NOTIFICATION : RingtoneManager.TYPE_RINGTONE);  
6:             int notif_icon = (notif_data[0].equalsIgnoreCase("0")) ? android.R.drawable.ic_dialog_alert : android.R.drawable.ic_dialog_info;  
7:             NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)  
8:             .setSound(ringToneUri)  
9:             .setSmallIcon(notif_icon)  
10:             .setContentTitle(notif_data[2])  
11:                      .setContentText(notif_data[3])  
12:                      .setAutoCancel(true)  
13:                      .setStyle(new NotificationCompat.InboxStyle());  
14:             Notification notification = mBuilder.build();  
15:             notificationManager.notify(Integer.parseInt(notif_data[1]), notification);  
16:       }  
17:  }   

When sending notifications, you can set the first part of the extra value (notif_type) to 0 or any number to change the icon and the tone played for the notification.

To view the complete source for my test application go here.