UOC Maps The free UOC Maps app for iOS / Android provides a virtual forum for members of the UOC community around the world, letting them find each other, communicate and express themselves. This social tool aims to promote community spirit and a...

UOC Maps

The free UOC Maps app for iOS / Android provides a virtual forum for members of the UOC community around the world, letting them find each other, communicate and express themselves. This social tool aims to promote community spirit and a feeling of belonging. It is designed to aid communication between members, help people get to know each other and establish professional and personal links within the community.

GCM push notifications for Titanium (made easy)

UPDATE 15/09/2015: Let’s see if this works for you. It’s exactly the same version of the published module -neither updates nor API changes-, just changed some stuff in order to compile w/ latest SDK and used some hackery or java black magic in order to make it work (FYI that evil thing is called Java reflections).
If you really want to know why it fails, it does since they started to use NotificationCompat, which is a good thing.. but the notification builder needs some context and they’re doing it wrong because they’re passing the current activity as a context. If the app is not active, there’s no current activity, so they’re passing null value, so it crash. I see that the problem is solved in Appcelerator’s Github master branch, but still happens in 4_1_X branch. So, when they include the patch in their official SDK, whatever you’re going to read next won’t be necessary:


var 
notification = Titanium.Android.createNotification({
    contentTitle: 'Notification 2',
    contentText : 'Just another notification',
    contentIntent: Ti.Android.createPendingIntent({intent: Ti.Android.createIntent({})}),
    when: new Date().getTime()
});
console.log(gcm.blackMagic(notification) ? "OK!" : "WRONG!");

Notice the blackMagic method… you need to call this method as soon as you create the notification object. There is where the hackery happens. If it succeed w/ black magic returns true, else false. Easy. Just pass the notification an voilà, it should work. If you’re are (and I hope you are) curious, the code under the hood:


@Kroll.method
public boolean blackMagic(Object obj) {
	if (!obj.getClass().equals(NotificationProxy.class)) {
		return false;
	}
	
	boolean success = true;
	
	try {
		Field privateField = NotificationProxy.class.getDeclaredField("notificationBuilder");
		privateField.setAccessible(true);
		
		Builder notificationBuilder = new NotificationCompat.Builder(TiApplication.getInstance().getApplicationContext());
		privateField.set(obj, notificationBuilder);
	} 
	catch (Exception e) {
		e.printStackTrace();
		
		success = false;
	} 
	
	return success;
}

It’s a big hack, things should not be made like this, but hey… it’s not my fault! This kind of things makes me remember why I quit Titanium development. It is a serious bug (published but not tested, surely) and, although they solved it -we could also talk about the time they took to do the job-, they have not published officially in the SDK… About updating this module: you must know that Appcelerator is not making your life easier since they’re not providing an official way to embed Google Play Services in a module w/out more than possible errors (e.g. modules using different versions of the library, duplicated resources; etc.). So, in order to update the module we need to use newer versions of GCM, BUT newer versions are bundled inside the Play Services. Ideally? They should add Play services to their SDK, so both SDK and modules can use it. For this reason I won’t update this module, I really don’t like workarounding, I prefer to do things the right way.

——————–

UPDATE: This module is not maintained since more than 2 years ago. BTW, as it seems a lot of people is using it, some folks asked me to update it (for Ti SDK 4.0), no fork is improving it and Appcelerator has no intention to expose GCM stuff in its SDK -I cannot be more disappointed-, I’ll try to find some time to do it. I quit Titanium development long before I stopped working on this module, since then I’m doing pure native development both iOS and Android (which is something I recommend 100%), so this is somehow is going to be quite backward. Also I’m quite busy… no time to make this a paid module and try to offer some support, so I think the best and fair solution to support the module development is make it donationware. So, here it is:

——————–

gcm.js is an open source module for Titanium Android SDK that lets developers receive GCM push notifications in their Android apps.
It has a very simple API -almost identical to iOS!- yet flexible and powerful, as it executes Javascript whenever a notification is received, no matter whether the app is in foreground or background or even if it’s not running.
Another very useful feature of the module is that we’re going to be able to know if the user has clicked on a status bar notification.

If you’re a newbie with GCM, please read the GCM: Getting Started first!

Let’s start with a few lines of configuration. Open the tiapp.xml file of your Titanium project since we need to declare the module first. Once done, gcm.js only needs one property to make things work: the sender id. As you see in the following example, just fill the property value with that info:


<property name="GCM_sender_id" type="string">YOUR_SENDER_ID</property>
<modules>
	<module platform="android" version="0.2">net.iamyellow.gcmjs</module>
</modules>

Now, somewhere in your app you’ll need to register it for push notifications:


var gcm = require('net.iamyellow.gcmjs')

var pendingData = gcm.data;
if (pendingData && pendingData !== null) {
	// if we're here is because user has clicked on the notification
	// and we set extras for the intent 
	// and the app WAS NOT running
	// (don't worry, we'll see more of this later)
	Ti.API.info('******* data (started) ' + JSON.stringify(pendingData));
}

gcm.registerForPushNotifications({
	success: function (ev) {
		// on successful registration
		Ti.API.info('******* success, ' + ev.deviceToken);
	},
	error: function (ev) {
		// when an error occurs
		Ti.API.info('******* error, ' + ev.error);
	},
	callback: function () {
		// when a gcm notification is received WHEN the app IS IN FOREGROUND
		alert('hellow yellow!');
	},
	unregister: function (ev) {
		// on unregister 
		Ti.API.info('******* unregister, ' + ev.deviceToken);
	},
	data: function (data) {
		// if we're here is because user has clicked on the notification
		// and we set extras in the intent 
		// and the app WAS RUNNING (=> RESUMED)
		// (again don't worry, we'll see more of this later)
		Ti.API.info('******* data (resumed) ' + JSON.stringify(data));
	}
});

// in order to unregister:
// require('net.iamyellow.gcmjs').unregister();

If you ever worked with iOS Push notifications in Titanium you’re going to find the API very familiar, right? But… wait! what happens if the app is in background? That’s pretty simple: we’re going to create a new file called gcm.js in our project ‘Resources’ directory, I mean where app.js is. When the app receives a notification, the javascript in gcm.js is going to be executed as an Android service. We can do whatever we want there: save data in database, or store some Ti.App.Properties, etc. but obviously whatever not GUI related.
It’s important to understand that Android fires an IntentService as soon as the device receives a GCM message notification, no matter if the app is running or not, that’s the point, isn’t it?! gcm.js 'fires’ our javascript code there as well, so yes, it also works if the app is not running!.
IMPORTANT NOTICE: starting in Android SDK 3.1, if the user force stop the app, a manual app invocation must be done in order to be able to receive GCM notifications again. Check this out.

Since the most common stuff we’re going to do with gcm.js is showing visual notifications appearing on the status bar, and I know sometimes it’s hard to understand the many flags there are, I wrote an example for just show the visual notification:


/*global Ti: true, require: true */

(function (service) {

	var serviceIntent = service.getIntent(),
	title = serviceIntent.hasExtra('title') ? serviceIntent.getStringExtra('title') : '',
	statusBarMessage = serviceIntent.hasExtra('message') ? serviceIntent.getStringExtra('message') : '',
	message = serviceIntent.hasExtra('message') ? serviceIntent.getStringExtra('message') : '',
	notificationId = (function () {
		// android notifications ids are int32
		// java int32 max value is 2.147.483.647, so we cannot use javascript millis timpestamp
		// let's make a valid timed based id:

		// - we're going to use hhmmssDYLX where (DYL=DaysYearLeft, and X=0-9 rounded millis)
		// - hh always from 00 to 11
		// - DYL * 2 when hour is pm
		// - after all, its max value is 1.159.597.289

		var str = '',
		now = new Date();

		var hours = now.getHours(),
		minutes = now.getMinutes(),
		seconds = now.getSeconds();
		str += (hours > 11 ? hours - 12 : hours) + '';
		str += minutes + '';
		str += seconds + '';

		var start = new Date(now.getFullYear(), 0, 0),
		diff = now - start,
		oneDay = 1000 * 60 * 60 * 24,
		day = Math.floor(diff / oneDay); // day has remaining days til end of the year
		str += day * (hours > 11 ? 2 : 1);

		var ml = (now.getMilliseconds() / 100) | 0;
		str += ml;

		return str | 0;
	})();
		
	// create launcher intent
	var ntfId = Ti.App.Properties.getInt('ntfId', 0),
	launcherIntent = Ti.Android.createIntent({
		className: 'net.iamyellow.gcmjs.GcmjsActivity',
		action: 'action' + ntfId, // we need an action identifier to be able to track click on notifications
		packageName: Ti.App.id,
		flags: Ti.Android.FLAG_ACTIVITY_NEW_TASK | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP
	});
	launcherIntent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
	launcherIntent.putExtra("ntfId", ntfId);

	// increase notification id
	ntfId += 1;
	Ti.App.Properties.setInt('ntfId', ntfId);

	// create notification
	var pintent = Ti.Android.createPendingIntent({
		intent: launcherIntent
	}),
	notification = Ti.Android.createNotification({
		contentIntent: pintent,
		contentTitle: title,
		contentText: message,
		tickerText: statusBarMessage,
		icon: Ti.App.Android.R.drawable.appicon,
		flags: Ti.Android.FLAG_AUTO_CANCEL | Ti.Android.FLAG_SHOW_LIGHTS
	});
	Ti.Android.NotificationManager.notify(notificationId, notification);

	service.stop();

})(Ti.Android.currentService);

I’ve commented the code as you can see (but it’s fine if you comment on this post asking for some help). Notice that we’re using intent 'extras’ in order to receive data from our server. But… wait again! what is that 'net.iamyellow.gcmjs.GcmjsActivity’ thing in the className property of the launcher intent? Since we don’t know when the user is going to click on the notification, we’re not able to know if the app is not running, or in foreground or in background. So, we need a man in the middle activity smart enough to forward the user to the correct Titanium activity. But our man in the middle could do more job! He’s going to collect the data associated to the notification (through the intent extras). We could do the same job with the stock Titanium API, but then we would need to add a lot of event listeners in order to 'catch’ the intent extras… actually, I decided to add this feature to the module to make my apps logic more simple, since all the job is centralized in only one place. So, and where is that place? Just create a new file called gcm_activity.js inside 'Resources’ directory, like we did before. Then let’s do something like this:


/*global Ti: true, require: true */

(function (activity, gcm) {

	var intent = activity.intent;

	// HERE we catch the intent extras of our notifications
	if (intent.hasExtra('ntfId')) {
		// and then we'll use 'data' property to pass info to the app (see pendingData lines of the 1st snippet)
		gcm.data = {
			ntfId: intent.getIntExtra('ntfId', 0)
		};
	}
	
	// 'isLauncherActivity' is a module property which tell us if the app is not running
	if (gcm.isLauncherActivity) {
		// if the app is not running, we need to start our app launcher activity
		// (launcher activity shows the splash screen and setup your app environment, so we need this)
		var mainActivityIntent = Ti.Android.createIntent({
			// 'mainActivityClassName' is another module property with name of our app launcher activity
			className: gcm.mainActivityClassName,
			packageName: Ti.App.id,
			flags : Ti.Android.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP
		});	
		mainActivityIntent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
		activity.startActivity(mainActivityIntent);
	}
	else {
		// if the app is running (is being resumed), just finish this activity!
		activity.finish();
	}

})(Ti.Android.currentActivity, require('net.iamyellow.gcmjs'));

That’s all, now we can use a NodeJS instance as server to try it all:


/*global require: true, console: true, process: true */

(function (messageId, callback) {
	var _GCM = require('gcm').GCM,
	GCM = new _GCM('API_KEY'); // API KEY at Google APIs Console

	var message = {
		registration_id: 'USER_REGISTRATION_ID',
		'data.title': 'shephard: what lies in the shadow of the statue?',
		'data.message': '4 8 15 16 23 42',
		'data.sound': 'blacksmoke.mp3',
		collapse_key: messageId
	};

	GCM.send(message, function (err, messageId) {
		if (err) {
			console.error('error!');
		}
		callback(0);
	});
})((new Date()).getTime() + '', process.exit);

For a working example app, check both 'example’ and 'example_server’ directories out in the gcm.js github repo.Also you can download the zip in the 'dist’ directory and install the module as usual.

meteoarbúcies meteoarbúcies app now available for both iOS as an universal app and Android!
It shows real time data from a Vantage Pro2 professional weather station placed at Arbúcies, Catalonia, including temperature, humidity, pressure and a graph...

meteoarbúcies

meteoarbúcies app now available for both iOS as an universal app and Android!
It shows real time data from a Vantage Pro2 professional weather station placed at Arbúcies, Catalonia, including temperature, humidity, pressure and a graph with 24 hour history for each measure.
The iOS version is optimized for iPhone 5 and retina iPads.

Websockets for Titanium (and socket.io)

TiWS is a very simple Titanium module for both iOS and Android and, as you may guess, it let’s you open Websocket connections with remote servers.

Its API is event oriented and quite simple to understand as the following example shows:


var websocket = require('net.iamyellow.tiws').createWS();

websocket.addEventListener('open', function () {
	Ti.API.debug('websocket opened');
});

websocket.addEventListener('close', function (ev) {
	Ti.API.info('websocket closed');
});

websocket.addEventListener('error', function (ev) {
	// ev.error contains error description, if any
	Ti.API.error(ev.error);
});

websocket.addEventListener('message', function (ev) {
	// ev.data contains message data
	Ti.API.log(ev.data);
});

WS.open('ws://IP_OR_URL[:PORT]');

But I’m pretty sure that most of the users of this module (like me) needed a websocket implementation for Titanium in order to make socket.io work natively in Titanium. Well, I’ve already done that job! I forked the socket.io client and made a few changes to make it work with TiWS. Also added XHR polling using Titanium XHR client to have the chance to fall back on it whenever websocket connections are not possible / fails.

Before start, grab the lastest version of the socket.io client from here.
More info at:

Let’s make that TextField grow and shink

TiGrowingTextField is yet another simple iOS module for Titanium that let’s you create a text field which grows or shinks depending on the the content the user types, like iMessage, WhatsApp, etc.

The module wraps a third party library TiGrowingTextField uses a third party by Hans Pinckaers (Github).

A demo snippet showing all the config options:


var textField = require('net.iamyellow.tigrowingtextfield').createView({
    // layout stuff -it works as any other view-
    height: 40, // 40 is the minimum height, the view height won't be less than 40
    left: 10, right: 10,
    // growing / shrink
    minNumberOfLines: 1, // default = 1
    maxNumberOfLines: 10, // default = 3,
    // this shows the keyboard as soon the view (window) gains the focus (is opened)
    showKeyboardImmediately: true,
    // background stuff
    backgroundImage: 'images/MessageEntryInputField.png',
    backgroundLeftCap: 12,
    backgroundTopCap: 20,
    value: 'hellow',
    // appearance 
    appearance: Ti.UI.KEYBOARD_APPEARANCE_ALERT,
    // text stuff, works as Ti.UI.TextField
    autocorrect: false, // disables / enables autocorrection
    textAlign: 'left',
    font: {
        fontFamily: 'Georgia',
        fontSize: 12
    },
    color: '#333333'
});
// now, we're ready to add the view to a parent view / window