Category: Ionic

Android Notifications using Cordova and Ionic

So you want to add push notifications to your Cordova/Android/Ionic app? I’ll walk through it. Most of this entry applies to Cordova and Android, in case you’ve made the mistake of NOT using Ionic…

Basic Info

Android notifications work through Google GCM (Google Cloud Messaging). You can read the details at http://developer.android.com/google/gcm/index.html, but here’s the summary:

GCM Explained

Here’s the narrative version:  When your application starts up, the Android device registers with GCM server, which responds with a Registration ID.   The device then sends the registration ID to our Message Server, which stores it for future use.  Other information, such as the user’s name or location, can be sent for message targeting.

When we want to send out a notification, our Message Server sends the text to the GCM server, along with the registration ID and the API .  The GCM server relays info to the device.

The GCM Server

Setting up your Google GCM server should be a fairly quick process. It’s a simple mechanism from our perspective, but does a lot of the heavy lifting for us. Follow these directions to get it running: http://developer.android.com/google/gcm/gs.html. Be sure to make note of your GCM Project ID, and your API Key. The Project ID will go into your Android app configuration, for registration. The API Key is used to send notifications.

The Notification Server

There are any number of ways to do this, and from what I’ve seen, folks often use custom code for this. Of coruse there are some off the shelf products. But for development, I recommend the node-gcm project https://github.com/ToothlessGear/node-gcm. It’s a super-simple way to communicate with the GCM server. First install

npm install node-gcm

Next set up a node script similar to this one:

var gcm = require('node-gcm');
var message = new gcm.Message();

//API Server Key
var sender = new gcm.Sender('INSERT_YOUR_API_SENDER_KEY_HERE');
var registrationIds = [];

// Value the payload data to send...
message.addData('message', "Hello Cordova!");
message.addData('title','Push Notification Sample' );
message.addData('msgcnt','2'); // Shows up in the notification in the status bar
message.addData('soundname','beep.wav'); //Sound to play upon notification receipt - put in the www folder in app
message.collapseKey = 'demo';
message.delayWhileIdle = true; //Default is false
message.timeToLive = 3000;// Duration in seconds to hold in GCM and retry before timing out. Default 4 weeks (2,419,200 seconds) if not specified.

// At least one reg id required
registrationIds.push('THIS_IS_THE_REGISTRATION_ID_THAT_WAS_GENERATED_BY_GCM');

/**
 * Parameters: message-literal, registrationIds-array, No. of retries, callback-function
 */
sender.send(message, registrationIds, 4, function (err, result) {
    console.log(result);
});

The Android App

As you know by now, you need a plugin to do just about anything in Cordova. So install the PushPlugin https://github.com/phonegap-build/PushPlugin. This should be the only plugin you need
for both Android and iOS. You might want to scan the instructions, but (as of now) they are both overly complicated and incomplete. For starters, you don’t need PlugMan to install. Just use:

cordova plugin add https://github.com/phonegap-build/PushPlugin.git

The coding was a little bit tricky, but I finally managed a functional script for Ionic notifications. See inline comments for more information on what’s going on here:

 //factory for processing push notifications.
angular.module('pushnotification', [])
   .factory('PushProcessingService', function() {
        function onDeviceReady() {
            console.info('NOTIFY  Device is ready.  Registering with GCM server');
            //register with google GCM server
            var pushNotification = window.plugins.pushNotification;
            pushNotification.register(gcmSuccessHandler, gcmErrorHandler, {"senderID":gcmAppID,"ecb":"onNotificationGCM"});
        }
        function gcmSuccessHandler(result) {
            console.info('NOTIFY  pushNotification.register succeeded.  Result = '+result)
        }
        function gcmErrorHandler(error) {
            console.error('NOTIFY  '+error);
        }
        return {
            initialize : function () {
                console.info('NOTIFY  initializing');
                document.addEventListener('deviceready', onDeviceReady, false);
            },
            registerID : function (id) {
                //Insert code here to store the user's ID on your notification server.
                //You'll probably have a web service (wrapped in an Angular service of course) set up for this.
                //For example:
                MyService.registerNotificationID(id).then(function(response){
                    if (response.data.Result) {
                        console.info('NOTIFY  Registration succeeded');
                    } else {
                        console.error('NOTIFY  Registration failed');
                    }
                });
            },
            //unregister can be called from a settings area.
            unregister : function () {
                console.info('unregister')
                var push = window.plugins.pushNotification;
                if (push) {
                    push.unregister(function () {
                        console.info('unregister success')
                    });
                }
            }
        }
    });

// ALL GCM notifications come through here.
function onNotificationGCM(e) {
    console.log('EVENT -> RECEIVED:' + e.event + '');
    switch( e.event )
    {
        case 'registered':
            if ( e.regid.length > 0 )
            {
                console.log('REGISTERED with GCM Server -> REGID:' + e.regid + "");

                //call back to web service in Angular.
                //This works for me because in my code I have a factory called
                //      PushProcessingService with method registerID
                var elem = angular.element(document.querySelector('[ng-app]'));
                var injector = elem.injector();
                var myService = injector.get('PushProcessingService');
                myService.registerID(e.regid);
            }
            break;

        case 'message':
            // if this flag is set, this notification happened while we were in the foreground.
            // you might want to play a sound to get the user's attention, throw up a dialog, etc.
            if (e.foreground)
            {
                //we're using the app when a message is received.
                console.log('--INLINE NOTIFICATION--' + '');

                // if the notification contains a soundname, play it.
                //var my_media = new Media("/android_asset/www/"+e.soundname);
                //my_media.play();
                alert(e.payload.message);
            }
            else
            {
                // otherwise we were launched because the user touched a notification in the notification tray.
                if (e.coldstart)
                    console.log('--COLDSTART NOTIFICATION--' + '');
                else
                    console.log('--BACKGROUND NOTIFICATION--' + '');

                // direct user here:
                window.location = "#/tab/featured";
            }

            console.log('MESSAGE -> MSG: ' + e.payload.message + '');
            console.log('MESSAGE: '+ JSON.stringify(e.payload));
            break;

        case 'error':
            console.log('ERROR -> MSG:' + e.msg + '');
            break;

        default:
            console.log('EVENT -> Unknown, an event was received and we do not know what it is');
            break;
    }
}

Call it from here:

app.run(function(PushProcessingService) {
   //run once for the app
   PushProcessingService.initialize();
});

Hopefully this is helpful to someone!

Thanks to these folks (and others) who put together helpful blogs on this topic:

Cordova and AngularJS – Opening links in system browser

In the course of writing my Android/Cordova/Ionic app, I ran into another tricky requirement: Display HTML content from a web service, containing links. These links should open in the user’s preferred browser. In the end, there are several steps necessary to getting this right.

Step 1: Display unescaped HTML in the app.

When AngularJS binds content, special characters are “escaped”, so HTML tags are visible and of course non-functional. Fortunately AngularJS has a fix for this: ng-bind-html. Use this instead of ng-bind (which is equivalent to the double-bracket method). Your content will be slightly sanitized (which will be a problem later), but the links are preserved.

<div ng-bind-html="content.Description"></div>

At this point you’ll see some strange behavior: the links open inside webkit. So the user stays in your app, but there is no way to go back to your Angular HTML5 pages. In my case, even the back button did not work!

As with so many Cordova issues, “there’s a plugin for that.”

Step 2: Install the inappbrowser plugin

Despite its name, the inappbrowser plugin enables the behavior we’re looking for – opening a browser window EXTERNAL to our application. The following command will install it:

cordova plugin add org.apache.cordova.inappbrowser

In theory, all you have to do is set up your links as follows:

window.open('http://intown.biz', '_system', 'location=yes');
Take Note: There might be a better solution for step 3, that does not require Jquery. See comment by Rewon below.

 

Step 3: Apply the “inappbrowser” approach (part 1).

The next question is how to apply this to your downloaded content? Because of AngularJS, there are two issues:

  • JavaScript is “sanitized” (i.e. not rendered) by ng-bind-html. Otherwise, we would be able to write a filter to replace our ordinary Anchor tags with tags that invoke the javascript you see above.
  • I tried directives, but since I don’t have control over the HTML being passed in, that proved more complicated.  Depending on your circumstances, a directive may work for you.

I finally arrived at a two step process. First, use a simple custom filter to add ‘class=”ex-link”‘ to each section where we want the links to open in the external browser.

.filter('externalLinks', function() {
   return function(text) {
     return String(text).replace(/href=/gm, "class=\"ex-link\" href=");
   }
 })

The above filter would need to be more sophisticated if there are classes within the HTML that need to be preserved. Applying the filter in AngularJS, your HTML now looks like this:

<div ng-bind-html="content.Description | externalLinks"></div>

Step 3: Apply the “inappbrowser” approach (part 2).

Finally, we address the question of how to force our links to open in the _system window using JavaScript. For this we’ll resort to JQuery. I generally like to keep JQuery out of my projects, but in my case it was already included for a third-party tool. If anyone knows of a “pure Angular” approach to this, I’m all ears.

Using $timeout ensures that this handler is one of the last applied to the page.

$timeout(function () {
   $('.ex-link').click(function () {
     var url = $(this).attr('href');
     window.open(encodeURI(url), '_system', 'location=yes');
     return false;
   })
})

You might place the above code into the controller of the page that needs it, or in a factory containing utility functions. Don’t forget to inject $timeout in the appropriate place.

This two-step approach has advantages: obviously the “ex-link” class can be added directly to a template’s HTML, allowing you to place an “external link” anywhere in your app, and distinguish from your app’s internal links.

Of course there are some risks to displaying foreign HTML in a Cordova app. For my project, I have been assured that the HTML will be correct, but of course if it’s not, my app could break. So down the road I may add HTML integrity checking, but in the Agile tradition, I will only address this if it’s a real issue.

Running Your Android/Cordova App

Running the app

If this is your first Android/Cordova app, you’re going to find out pretty quickly (like in the first 5 minutes) that you’ll need a place to run it. You have a few options:

On Your Phone

It is easy to run the app on your own Android phone.  I’m listing this option first because it is reasonably quick, and quicker than the SDK emulator.   First go to Settings, turn on Developer Options and make sure USB Debugging is checked.    You can then connect to your development machine with a USB and execute:

cordova run android

Android SDK Emulators/Virtual Devices

The Android SDK comes with a variety of emulators, none of which are initially installed.  First run the Android Virtual Devices manager:

android avd

Here you can create one or more Virtual Devices which emulate different phones.  Add a new device and configure it.   Virtual devices can also be downloaded from smart phone manufacturers, if you want to be specific in your testing.  The following command will build and run your app on the virtual device:

cordova emulate android

Of course you can specify which virtual device you want to start up, or use the default.

This emulator is probably the most accurate representation of a phone that you can run, without actually installing on a real one.   But it is very slow to start up and run.  I found myself looking for an alternative almost immediately.

Just Use the Browser

It quickly occurred to me that because I was writing an HTML/JavaScript app (Cordova) that I could just run it in the browser.  So I chose my favorite node.js based lightweight server and was up and running in a few minutes.  This worked fine for basic functionality, and of course is very handy for addressing JavaScript issues.  This is the world I expect to work in, for most of my app’s functionality, because I’m using Ionic/AngularJS.

However this approach has a few disadvantages:  it’s not going to win any awards for appearance – I could not use this method for a client demo or screen shots.   And worst of all, it is ONLY for web functionality.

Also, I ran into an issue which didn’t show up here.  Of course.  It’s not running any of the Cordova scaffolding, so the minute you start changing configuration settings or want to check permissions, you need something more “real”.   I expect this method to break if I write custom extensions or need other features that chrome does not offer.

Ripple

I have used Ripple on a limited basis, but it appears to be a nice compromise between the incredibly slow SDK emulator and the “just run in browser” approach.

If you’re new to Ripple, here’s the 30 second low-down: It runs within Chrome, but you start it from the command line.   Do NOT install the chrome extension.  Just install it from npm as follows:

npm install -g ripple-emulator

You’ll still need to run in your phone or the SDK emulator, and I suggest doing so whenever you make a change to the configuration or underlying Java.  For example, the renaming problem I encountered did not show up in Ripple, though it did in the SDK emulator.

In summary, I expect you’ll be looking at your app using a variety of methods, depending on what you’re testing or need to do.    Let me know if I’ve left anything out, and good luck!

AngularJS/Cordova mobile app Dev Setup – Part 2

In AngularJS/Cordova mobile app Dev Setup – Part 1 we got things rolling.  We now have node.js, Cordova and associated tools in our environment.  Now to apply AngularJS and Ionic.

Part 1 will get you up and running with Cordova, from scratch.
Part 2 adds AngularJS and Ionic to the mix
Part 3 running your app

Ionic is a package of components including AngularJS, custom Angular directives, and some styles which give our HTML5 nice “mobile” look.   Out of the box, I found that my app was fairly true to the “iphone look”. Although I’m writing an Android application, this app might eventually be used for iPhone, so this suits me fine.

Ionic

Ionic is very simple to get started.  We’re just going to assume that you followed the steps in part 1.

  • Install Ionic
  • npm install -g ionic
  • Create an Ionic app
    ionic start myApp

Now you’re ready to run the sample code.   This can be done using Cordova to deploy to the emulator or your phone.  This code is also simple enough that you can also run it in a browser!

You can use Express or another lightweight brower to do this, by opening up the www directory [TODO: more detail].

Going the next step

You now have a start on your project, but there are still some tools that you will probably need.   I ended up adding these tools shortly after creating it, so here they are:

  • Create a package.json file.
  • Grunt, a scripting framework for node.js.  First you’ll need the command line (installed gloablly), and then install to the project (saving to json file)
    npm install -g grunt-cli
    npm install grunt --save-dev
  • Bower, a packaging manager, similar to npm but intended to manage external tools used in your app, rather than in your dev environment.
     npm install -g bower

Many bower packages require you to install git as well (for example, angular-google-maps).  When  installing for windows, I selected the “add to path” option.  This makes it possible to run bower from a standard MS-DOScommand line (you don’t have to use git/bash).

Enjoy Angular

Now it’s time to poke around with Angular and add some functionality.  Good luck!

AngularJS/Cordova mobile app Dev Setup – Part 1

The Cordova framework (pretty much synonymous with PhoneGap) puts an HTML5 “wrapper” over the native code (for ios or Android), working toward the goal of write-once-run-anywhere code.   Instead of writing in Objective C or Java, most of the code is written in HTML5 and Javascript.  I’ve been using AngularJS on a couple of other projects, so naturally I wondered if anyone had applied it to mobile/Cordova/PhoneGap applications.  Sure enough I found Ionic, a framework composed of AngularJS, custom Angular directives, and some styles which make things go together nicely.

Part 1 will get you up and running with Cordova, from scratch.
Part 2 adds AngularJS and Ionic to the mix
Part 3 running your app

My first task was to set up my environment.  Sure, all the tools were out there, but I spent more time piecing things together than I would have liked.  It would have helped me if there had been a Getting Started guide for a first-time mobile developer.   So here goes…

I use a Windows 8 machine, so it’s fortunate that I’m designing primarily an Android app.  It seems that a Mac is the best choice because you can develop to any mobile platform on it.  But I’ll try to keep my development software platform agnostic so that an ios dev could grab the code and use it.

My approach was to get first get Cordova set up and tested, and then apply the AngularJS scaffolding.

Generic Cordova Dev Environment Setup

Install

  • node.js – this includes the npm package manager, and is the gateway to a world of open source tools and packages
  • Java JDK  – don’t laugh, but this was not on my machine yet.  Hey, it’s a new Windows 8!  Make sure to download the Java Development Kit (JDK).  The JRE does not have development tools.
  • Apache ANT – A scripitng framework http://ant.apache.org/bindownload.cgi
  • Android Developer SDK – Lower-level stuff for Android.  http://developer.android.com/sdk/
  • Cordova – Now you’ll start using the package manager you installed with node.js.  Open up a command line and run:
    npm install -g cordova

Settings

Open up your Computer Properties/Advanced System Settings, then click on Environment Variables.  Add the following variables (substitute your own for mine):

ANT_HOME = C:\bin\apache-ant-1.9.3
ANDROID_HOME = C:\bin\adt-bundle\sdk
JAVA_HOME = C:\Program Files\Java\jdk1.7.0_51

Then add the following line to the end of the path:

%JAVA_HOME%\bin;%ANT_HOME%\bin;cordo%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools

Creating a project

If I haven’t left anything out above (and if you haven’t), you should be ready to use the above tools.   If you’re confident and want to go straight to the Angular/Ionic Setup, go for it.  Otherwise open a command line and type the following:

> cordova create hello com.example.hello "HelloWorld"
> cd hello
> cordova platform add android
> cordova build

This will create a boilerplate project, a very simple hello world application.  The next commands modify the project by adding android support to it, and build it.

A word about IDE’s

The Android Developer SDK comes with the Eclipse editor.  This works very nicely for Java, but (because we’ll be using Cordova) we’re going to be working mostly with HTML and Javascript.  I chose JetBrains WebStorm, because (despite being a commercial product) it works well for HTML and JavaScript, and extends nicely to AngularJS.  Full featured and inexpensive.

Of course I eventually expect the need for a full featured Java editor later in the game.  For that I plan to go back to Eclipse.