Working with Ionic and PHP Backends. Remote debugging with PHP7 and Xdebug working with real devices

Sometimes I speak with PHP developers and they don’t use remote debugging in their development environments. Some people don’t like to use remote debugging. They prefer to use TDD and rely on the unit tests. That’s a good point of view, but sometimes they don’t use remote debugging only because they don’t know how to do it, and that’s inadmissible. Remote debugger is a powerful tool especially to handle with legacy applications. I’ve using xdebug for years with my linux workstation for years. This days I’m using Mac and it’s also very simple to set up xdebug here.

First we need to install PHP:

brew install php70

Then Xdebug

brew install php70-xdebug

(in a Ubuntu box we only need to use apt-get instead of brew)

Now we need to setup xdebug to enable remote debugging:
In a standard installation xdebug configuration is located at: /usr/local/etc/php/7.0/conf.d/ext-xdebug.ini

[xdebug]
zend_extension="/usr/local/opt/php70-xdebug/xdebug.so"

xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.profiler_enable=0
xdebug.profiler_output_dir="/tmp"
xdebug.idekey= "PHPSTORM"
xdebug.remote_connect_back = 1
xdebug.max_nesting_level = 250

And basically that’s all. To set/unset the cookie you can use one bookmarklet in your browser (you can generate your bookmarklets here). Or use a Chrome extension to enable xdebug.

Now se only need to start the built-in server with

php -S 0.0.0.0:8080

And remote debugging will be available
Remote debugger works this way:

  • We open on port within our IDE. In my case PHPStorm (it happens when we click on “Start listening for PHP debug connections”)
  • We set one cookie in our browser (it happens when click on Chrome extension)
  • When our server receives one request with the cookie, it connects to the port that our IDE opens (usually port 9000). If you use a personal firewall in your workstation, ensure that you allow incoming connections to this port.

Nowadays I’m involved with several projects building hybrid applications with Apache Cordova. In the Frontend I’m using ionic and Silex in the Backend. When I’m working with hybrid applications normally I go through two phases.

In the first one I build a working prototype. To to this I run a local server and I use my browser to develop the application. This phase is very similar than a traditional Web development process. If we also set up properly LiveReload, our application will be reloaded each time we change one javaScript file. Ionic framework integrates LiveReload and we only need to run:

ionic serve -l

to start our application. We also need to start our backend server. For example

php -S 0.0.0.0:8080 -t api/www

Now we can debug our Backend with remote debugger and Frontend with Chrome’s developer’s tools. Chrome also allows us to edit Frontend files and save them within the filesystem using workspaces. This phase is the easy one. But sooner or later we’ll need start working with a real device. We need a real device basically if we use plugins such as Camera plugin, Geolocation plugin, or things like that. OK there are emulators, but usually emulators don’t allow to use all plugins in the same way than we use then with a real device. Chrome also allow us to see the console logs of the device from our workstation. OK we can see all logs of our plugged Android device using “adb logcat” but follow the flow of our logs with logcat is similar than understand Matrix code. It’s a mess.

If we plug our android device to our computer and we open with Chrome:

chrome://inspect/#devices

We can see our device’s console, use breakpoints and things like that. Cool, isn’t it? Of course it only works if we compile our application without “–release” option. We can do something similar with Safary and iOS devices.

With ionic if we want to use LiveReload from the real device and not to recompile and re-install again and again our application each time we change our javaScript files, we can run the application using

ionic run android --device -l

When we’re developing our application and we’re in this phase we also need to handle with CORS. CORS isn’t a problem when we run our hybrid application in production. When we run the hybrid application with our device our “origin” is the local filesystem. That’s means CORS don’t apply, but when we run our application in the device, but served from our computer (when we use “-l” option), our origin isn’t local filesystem. So if our Backend is served from another origin we need to enable CORS.

We can enable CORS in the backend. I’ve written about it here, but ionic people allows us a easier way. We can set up a local proxy to serve our backend through the same origin than the application does and forget about CORS. Here we can read a good article about it.

Anyway if we want to start the remote debugger we need to create one cookie called XDEBUG_SESSION. In the browser we can use chrome extension, but when we inspect the plugged device isn’t so simple. It would be cool that ionic people allows us to inject cookies to our proxy server. I’ve try to see how to do it with ionic-cli. Maybe is possible but I didn’t realize how to do it. Because of that I’ve created a simple AngularJS service to inject this cookie. Then, if I start listening debug connections in my IDE I’ll be able to use remote debugger as well as I do when I work with the browser.

First we need to install service via Bower:

bower install ng-xdebugger --save

Now we need to include javaScript files

<script src="lib/angular-cookies/angular-cookies.min.js"></script>
<script src="lib/ng-xdebugger/dist/gonzalo123.xdebugger.min.js"></script>

then we add our service to the project.

angular.module("starter", ["ionic", "gonzalo123.xdebugger"])

Now we only need to configure our application and set de debugger key (it must be the same key than we use within the server-side configuration of xdebug)

.config(function (xdebuggerProvider) {
        xdebuggerProvider.setKey('PHPSTORM');
    })
})

And that’s all. The service is very simple. It only uses one http interceptor to inject the cookie in our http requests:

(function () {
    "use strict";

    angular.module("gonzalo123.xdebugger", ["ngCookies"])
        .provider("xdebugger", ['$httpProvider', function ($httpProvider) {
            var debugKey;

            this.$get = function () {
                return {
                    getDebugKey: function () {
                        return debugKey;
                    }
                };
            };

            this.setKey = function (string) {
                if (string) {
                    debugKey = string;
                    $httpProvider.interceptors.push("xdebuggerCookieInterceptor");
                }
            };
        }])

        .factory("xdebuggerCookieInterceptor", ['$cookieStore', 'xdebugger', function ($cookieStore, xdebugger) {
            return {
                response: function (response) {
                    $cookieStore.put("XDEBUG_SESSION", xdebugger.getDebugKey());

                    return response;
                }
            };
        }])
    ;
})();

And of course you can see the whole project in my github account.

Advertisement

POST Request logger using websockets

Last days I’ve been working with background geolocation with an ionic application. There’s a cool plugin to do that. The free version of the plugin works fine. But there’s a also a premium version with improvements, especially in battery consumption with Android devices.

Basically this plugin performs a POST request to the server with the GPS data. When I was developing my application I needed a simple HTTP server to see the POST requests. Later I’ll code the backend to handle those requests. I can develop a simple Silex application with a POST route and log the request in a file or flush those request to the console. This’d have been easy but as far as I’m a big fan of WebSockets (yes I must admit that I want to use WebSockets everywere 🙂 I had one idea in my mind. The idea was create a simple HTTP server to handle my GPS POST requests but instead of logging the request I will emit a WebSocket. Then I can create one site that connects to the WebSocket server and register on screen the POST request. Ok today I’m a bit lazy to fight with the Frontend so my log will be on the browser’s console.

To build the application I’ll reuse one of my projects in github: The PHP dumper. The idea is almost the same. I’ll create a simple HTTP server with Silex with two routes. One to handle POST requests (the GPS ones) and another GET to allow me to connect to the WebSocket

That’s the server. Silex, a bit of Twig, another bit of Guzzle and that’s all

use GuzzleHttp\Client;
use Silex\Application;
use Silex\Provider\TwigServiceProvider;
use Symfony\Component\HttpFoundation\Request;

$app = new Application([
    'debug'       => true,
    'ioServer'    => '//localhost:8888',
    'wsConnector' => 'http://127.0.0.1:26300'
]);

$app->register(new TwigServiceProvider(), [
    'twig.path' => __DIR__ . '/../views',
]);

$app['http.client'] = new Client();

$app->get("/{channel}", function (Application $app, $channel) {
    return $app['twig']->render('index.twig', [
        'channel'  => $channel,
        'ioServer' => $app['ioServer']
    ]);
});

$app->post("/{channel}", function (Application $app, $channel, Request $request) {
    $app['http.client']->get($app['wsConnector'] . "/info/{$channel}/" . json_encode($request->getContent()));

    return $app->json('OK');
});

$app->run();

That’s the Twig template. Nothing especial: A bit of Bootstrap and one socket.io client. Each time user access to one “channel”‘s url (GET /mychannel). It connects to websocket server

var CONF = {
        IO: {HOST: '0.0.0.0', PORT: 8888},
        EXPRESS: {HOST: '0.0.0.0', PORT: 26300}
    },
    express = require('express'),
    expressApp = express(),
    server = require('http').Server(expressApp),
    io = require('socket.io')(server, {origins: 'localhost:*'})
    ;

expressApp.get('/:type/:session/:message', function (req, res) {
    console.log(req.params);
    var session = req.params.session,
        type = req.params.type,
        message = req.params.message;

    io.sockets.emit('dumper.' + session, {title: type, data: JSON.parse(message)});
    res.json('OK');
});

io.sockets.on('connection', function (socket) {
    console.log("Socket connected!");
});

expressApp.listen(CONF.EXPRESS.PORT, CONF.EXPRESS.HOST, function () {
    console.log('Express started');
});

server.listen(CONF.IO.PORT, CONF.IO.HOST, function () {
    console.log('IO started');
});

And each time background geolocation plugin POSTs GPS data Silex POST route will emit a WebSocket to the desired channel. Our WebSocket client just logs the GPS data using console.log. Is hard to explain but very simple process.

We also can emulate POST requests with this simple node script:

var request = require('request');

request.post('http://localhost:8080/Hello', {form: {key: 'value'}}, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        console.log(body)
    }
});

And that’s all. You can see the whole code within my github account.

Debugging android cordova/phonegap apps with Chrome

Maybe this post can be obvious but I’ve spoken about it with various developers who don’t know it. It really improves the developing process of cordova/phonegap apps with android at least for me.

With android we can see the log with “adb logcat” but it’s a nightmare. Huge amount of information about our app and also about the operating system. If we’re grep ninjas we can handle it, but as well as I’m not a ninja I prefer another solution. Do you know “chrome://inspect/”? If not, have a look as soon as possible to this tool. We can see the browser’s console of our android in our desktop browser. We only need to enable “usb remote debugger” within our android device and plug with a USB cable. Chrome will detect the remote browser and we can see the console in the same way than we see it when we use Chrome locally.

But we’re speaking about cordova/phonegap apps here so, what we need to do to use chrome://inspect with our hybrid apps? The answer is simple: we don’t need to do anything. Cordova applications is nothing than a Webkit browser inside a native app. Chrome es Webkit too so chrome://inspect will detect our remote device app and we will open console.

Inspect_with_Chrome_Developer_Tools_and_bad_religion-the_gray_race

This small trick in addition to the last post really marks a before and an after at least in my developing process.

If our app crashes in the device we only need to see the console’s log within our browser and see what happens. We also can add functionality, change variables, and override functions in the same way than we do it with our local browser.

Upgrading Cordova-Android apps outside Google Play Store with angularjs

Recent months I’ve working with enterprise mobile applications. This apps are’t distributed using any marketplace, so I need to handle the distributions process. With Android you can compile your apps, create your APK files and distribute them. You can send the files by email, use a download link, send the file with bluetooth, or whatever. With iOS is a bit different. You need to purchase one Enterprise license, compile the app and distribute your IPA files using Apple’s standards.

OK, but this post is not about how to distribute apps outside the markets. This post is about one big problem that appears when we need to upgrade our apps. How do the user knows that there’s a new version of the application and he needs to upgrade? When we work inside Google Play Store we don’t need to worry about it, but if we distribute our apps manually we need do something. We can send push notifications or email to the user to inform about the new version. Let me show you how I’m doing it.

My problem isn’t only to let know to the user about a new version. Sometimes I also need to ensure that the user runs the last version of the app. Imagine a critical bug (solved in the last release) but the user don’t upgrade.

First we need to create a static html page where the user can download the APK file. Imagine that this is the url where the user can download the last version of the app:

http://192.168.1.1:8888/app.apk

We can check the version of the app against the server each time the user opens the application, but this check means network communication and it’s slow. We need to reduce the communication between client and server to the smallest expression and only when it’s strictly necessary. Check the version each time can be good in a desktop application, but it reduces the user experience with mobile apps. My approach is slightly different. Normally we use token based authentication within mobile apps. That’s means we need to send our token with all request. If we send the token, we also can send the version.

In a angular app we can define the version and the path of our apk using a key-value store.

.value('config', {
        version: 4,
        androidAPK: "http://192.168.1.1:8888/app.apk"
    })

Now we need to add version parameter to each request (we can easily create a custom http service to append this parameter to each request automatically, indeed)

$http.get('http://192.168.1.1:8888/api/doSomething', {params: {_version: config.version}})
    .success(function (data) {
        alert("OK");
    })
    .error(function (err, status) {
        switch (status) {
            case 410:
                $state.go('upgrade');
                break;
        }
    });

We can create a simple backend to take care of the version and throws an HTTP exception (one 410 HTTP error for example) if versions doesn’t match. Here you can see a simple Silex example:

<?php

include __DIR__ . "/../vendor/autoload.php";

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;

$app = new Application([
    'debug'   => true,
    'version' => 4,
]);

$app->after(function (Request $request, Response $response) {
    $response->headers->set('Access-Control-Allow-Origin', '*');
});

$app->get('/api/doSomething', function (Request $request, Application $app) {
    if ($request->get('_version') != $app['version']) {
        throw new HttpException(410, "Wrong version");
    } else {
        return $app->json('hello');
    }
});

$app->run();

As you can see we need to take care about CORS

With this simple example we can realize if user has a wrong version within each server request. If version don’t match we can, for example redirect to an specific route to inform that the user needs to upgrade the app and provide a link to perform the action.

With Android we cannot create a link to APK file. It doesn’t work. We need to download the APK (using FileTransfer plugin) and open the file using webintent plugin.

The code is very simple:

var fileTransfer = new FileTransfer();
fileTransfer.download(encodeURI(androidUrl), 
    "cdvfile://localhost/temporary/app.apk",
    function (entry) {
        window.plugins.webintent.startActivity({
            action: window.plugins.webintent.ACTION_VIEW,
            url: entry.toURL(),
            type: 'application/vnd.android.package-archive'
        }, function () {
        }, function () {
            alert('Failed to open URL via Android Intent.');
            console.log("Failed to open URL via Android Intent. URL: " + entry.fullPath);
        });
    }, function (error) {
        console.log("download error source " + error.source);
        console.log("download error target " + error.target);
        console.log("upload error code" + error.code);
    }, true);

And basically that’s all. When user self-upgrade the app it closes automatically and he needs to open it again, but now with the correct version.

Testing Phonegap/Cordova applications fast as hell in the device (with ionic framework)

Normally when we work with Phonegap/Cordova applications we work in two phases. First we develop the application locally using our browser. That’s “fast” phase. We change something within our code, then we reload our browser and we see the outcome. It isn’t different from a “traditional” web developing process. Normally I use the ionic framework. Ionic is great and it also provides us a good tool to run a local server. We just type:

ionic serve

And ionic starts a local server on port 8100 with our Cordova application, ready to test with the browser (it also opens the browser). That’s not the cool part. Ionic also starts a live reload server at http://0.0.0.0:35729 and adds the following snippet at the end of our index.html

<script type="text/javascript">//<![CDATA[
document.write('<script src="' + (location.protocol || 'http:') + '//' + (location.hostname || 'localhost') + ':35729/livereload.js?snipver=1" type="text/javascript"><\/script>')
//]]></script>

With this snippet our application will be reloaded when we add/remove something in our file tree (it runs a filesystem watcher in background).

But as I said before it’s the “fast” phase and sooner or later we will need to run the application in the real device. OK we’ve got emulators, but they are horrible. Android emulator is incredible slow. IOS one is faster but we need to redeploy the application again and again with each change. For example when we correct a silly bug we need to run the following command to see the application running on the device:

cordova run android --device

And it takes time (around 10 seconds). We’ve gone from the “fast” phase to the “slooooow” one. That means that I tried to avoid this phase until no remedy.

If you don’t use plugins you can let this “slow” phase to the end, only to see the behaviour in the device and fix customizations, but ir we use plugins (camera plugin, push notifications or things like that) we really need to test on the real device. Those kind of things doesn’t work in the browser or even with the emulator.

This “slow” phase droves me crazy, so I started to think a little bit about it. One Cordova app has two parts. The native one (java code in android and objective-c in ios) and the html/js part. We need to tell to our Cordova application where is the initial index.html. We usually do it in config.xml

<content src="index.html" />

But we can change this initial file and use a remote one. That’s the way to create a “native” app from and existing web application.

<content src="http://gonzalo123.com" />

According to this we can start a local server in our host and use this local web server. Even in our LAN (if our android/ios device is the LAN of course)

<content src="http://192.168.1.1:8100/index.html" />

But, what happens with the plugins? Plugins needs cordova.js file and this file isn’t in www folder. This file is generated when we build the application to a specific platform

platforms/android/assets/www/cordova.js

So, what’s the idea. The idea is:

  • Run a local server with (inoic serve for example)
  • Enable the fs watcher to restart the application when we change one file in the filesystem (inonic serve do it by default)
  • Build the application and install it in the real device
  • Use our local server to serve static files instead of build again and again the application with each change.

With this approach we only need to deploy the application to the real device when we want to add/remove one plugin. If we change anything in the static files (html, js, css) our app will be reloaded automatically. The “slow” phase turns into a “fast” phase.

How can we do it? It’s easy. In this example I suppose that we’re using one android device. If we use on iPhone we only need to change “adroid” to “ios”.

First of all we need to prepare our index.html to enable auto-reload. “ionic serve” do it automatically but it thinks that we’re going to use it with your host browser. Not with the “real” device. We can change it manually adding to our index.html (this snippet suppose that your host is 192.168.1.1 if it’s a different one use your local IP address):

    <script type="text/javascript">//<![CDATA[
    document.write('<script src="http://192.168.1.1:35729/livereload.js?snipver=1" type="text/javascript"><\/script>')
    //]]></script>

Now we change our config.xml to use our local server instead of device’s files:

<!--<content src="index.html" />-->
<content src="http://192.168.1.105:8100/index.html" />

Now we need to deploy the application to our device:

cordova run android --device

Each time we add/remove one plugin we need to redeploy to the device. But we need to keep in mind that our device will use the cordova.js from our local server, and not from its filesystem. “cordova run android –device” will generate the file to the platform and deploy them to the real device, but as well as we’re going to use this file from our local server (in www), we need to create a set of symlinks in our www folder.

(I’ve got one setUp.sh file with this commands)

cd www
ln -s ../platforms/android/assets/www/cordova.js
ln -s ../platforms/android/assets/www/cordova_plugins.js
ln -s ../platforms/android/assets/www/plugins
cd ..

Now can start the application’s server in our host with:

ionic serve --nobrowser

notice that we’re using –nobrowser. We’re using this parameter to not to open our local browser. We’re going to use de device’s Cordova’s Webkit one, and also if we open our browser it will crash because cordova.js is present now and our local host isn’t a real device.

Each time we need to redeploy the application to the device (new plugin for example) we need to remember to quit the symlinks, and redeploy.

(I’ve got one tearDown.sh file with this commands)

rm www/cordova.js
rm www/cordova_plugins.js
rm -Rf www/plugins 

And that’s all. I now that this little hack may looks like something difficult but we need less than a minute to set up the environment and we will save thousand of seconds in the development process. I we work a little bit we can automate this process and turn it into a trivial operation, but at least now I feel very comfortable.

Of course you need to remember to clean the project when you finish and use the device’s files. So we need to remove the auto-reload snippet in the index.html, remove symlinks and restore config.xml.

UPDATE:

ionic framework comes now with this feature out-of-the box. Just follow the instructions explained here:

Live Reload All the Things: Ionic CLI’s Latest Features

Token based authentication with Silex and AngularJS

According to my last post today we’re going to create a AngularJS application that uses the Silex Backend that we create previously. The idea of this application is to use it within a Phonegap/Cordova application running in a mobile device.

The application will be show a login form if device haven’t a correct token.

Gonzalo_Login_Example_and_LoginServiceProvider_php_-_token_-____work_projects_token_

And whit a correct token:

Gonzalo_Login_Example

Nothing new under the sun, isn’t it?

Our front-end application will use AngularJS and Topcoat.

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" lang="es" ng-app="G">
<head>
    <meta charset="utf-8"/>
    <meta name="format-detection" content="telephone=no"/>
    <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
    <meta name="viewport"
          content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"/>
    <link rel="stylesheet" type="text/css" href="/bower_components/topcoat/css/topcoat-mobile-light.min.css">
    <title>Gonzalo Login Example</title>
</head>
<body ng-controller="MainController">

<div ng-view class="main-content"></div>

<script src="/bower_components/angular/angular.min.js"></script>
<script src="/bower_components/angular-route/angular-route.min.js"></script>

<script src="js/app.js"></script>
<script src="js/services.js"></script>

</body>
</html>

And our AngularJS application:

'use strict';
var appControllers, G;
var host = 'http://localhost:8080'; // server API url

appControllers = angular.module('appControllers', []);
G = angular.module('G', ['ngRoute', 'appControllers']);

G.run(function (httpG) {
    httpG.setHost(host);
});

G.config(['$routeProvider', function ($routeProvider) {
    $routeProvider.
        when('/login', {templateUrl: 'partials/login.html', controller: 'LoginController'}).
        when('/home', {templateUrl: 'partials/home.html', controller: 'HomeController'});
}]);

appControllers.controller('HomeController', ['$scope', 'httpG', '$location', function ($scope, httpG, $location) {
    $scope.hello = function () {
        httpG.get('/api/info').success(function (data) {
            if (data.status) {
                alert("Hello " + data.info.name + " " + data.info.surname);
            }
        });
    };

    $scope.logOut = function () {
        alert("Good bye!");
        httpG.removeToken();
        $scope.isAuthenticated = false;
        $location.path('login');
    };
}]);

appControllers.controller('MainController', ['$scope', '$location', 'httpG', function ($scope, $location, httpG) {
    $scope.isAuthenticated = false;

    if (httpG.getToken()) {
        $scope.isAuthenticated = true;
        $location.path('home');
    } else {
        $location.path('login');
    }
}]);


appControllers.controller('LoginController', ['$scope', '$location', 'httpG', function ($scope, $location, httpG) {
    $scope.user = {};

    $scope.doLogIn = function () {
        httpG.get('/auth/validateCredentials', {user: $scope.user.username, pass: $scope.user.password}).success(function (data) {
            if (data.status) {
                httpG.setToken(data.info.token);
                $scope.isAuthenticated = true;
                $location.path('home');
            } else {
                alert("login error");
            }
        }).error(function (error) {
            alert("Login Error!");
        });
    };

    $scope.doLogOut = function () {
        httpG.removeToken();
    };
}]);

In this example I’m using angular-route to handle the application’s routes. Nowadays I’m swaping to angular-ui-router, but this example I’m still using “old-style” routes. We define two partials:

partial/home.html

<div class="topcoat-button-bar full" style="position: fixed; bottom: 0px;">
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button full" ng-click="logOut()">
            <span class="">Logout</span>
        </button>
    </label>
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button--cta full" ng-click="hello()">
            <span class="">Hello</span>
        </button>
    </label>
</div>

partial/login.html

<div class="topcoat-navigation-bar">
    <div class="topcoat-navigation-bar__item center full">
        <h1 class="topcoat-navigation-bar__title">Login</h1>
    </div>
</div>

<ul class="topcoat-list__container">
    <li class="topcoat-list__item center">
        <input ng-model="user.username" class="topcoat-text-input--large" type="text" name="user"
               placeholder="Username"/>
    </li>
    <li class="topcoat-list__item center">
        <input ng-model="user.password" class="topcoat-text-input--large" type="password" name="pass"
               placeholder="Password"/>
    </li>
</ul>

<div class="topcoat-button-bar full" style="position: fixed; bottom: 0px;">
    <label class="topcoat-button-bar__item">
        <button class="topcoat-button--cta full" ng-click="doLogIn()">
            <span class="">Login</span>
        </button>
    </label>
</div>

As we can see in the application we’re using a service to handle Http connections with the token information.

'use strict';

G.factory('httpG', ['$http', '$window', function ($http, $window) {
    var serviceToken, serviceHost, tokenKey;
    tokenKey = 'token';
    if (localStorage.getItem(tokenKey)) {
        serviceToken = $window.localStorage.getItem(tokenKey);
    }

    $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";

    return {
        setHost: function (host) {
            serviceHost = host;
        },

        setToken: function (token) {
            serviceToken = token;
            $window.localStorage.setItem(tokenKey, token);
        },

        getToken: function () {
            return serviceToken;
        },

        removeToken: function() {
            serviceToken = undefined;
            $window.localStorage.removeItem(tokenKey);
        },

        get: function (uri, params) {
            params = params || {};
            params['_token'] = serviceToken;
            return $http.get(serviceHost + uri, {params: params});
        },

        post: function (uri, params) {
            params = params || {};
            params['_token'] = serviceToken;

            return $http.post(serviceHost + uri, params);
        }
    };
}]);

And that’s all. You can see the full example in my github account.

Enabling CORS in a RESTFull Silex server, working with a phonegap/cordova applications

This days I’m working with phonegap/cordova projects. I’m using topcoat and AngularJs to build the client side and Silex for the backend. Cordova applications are “diferent” than a common web application. Our client side is normally located inside our mobile device (it’s also possible to use remote webviews). Our cordova application must speak with our backend. The easiest way to perform this operation is to use a REST. AngularJS has a great tool to connect with RESTFull resources. Silex is also great to build RESTFull services. I wrote a couple of posts about it.

With the first request form our AngularJS application (into our android/iphone device) to our Silex application, we will face with CORS. We cannot perform a request from our “local” phonegap/cordova application to our remote WebServer. We cannot do it if we don’t allow it explictily. With Silex it’s pretty straight forward to do it. We can use the event dispatcher and change the request with after handler.

$app->after(function (Request $request, Response $response) {
    $response->headers->set('Access-Control-Allow-Origin', '*');
});

We can do more strict, setting also “Access-Control-Allow-Methods” and “Access-Control-Allow-Headers” headers but only with this header we can work properly with our RESTFull Silex application from our phonegap/cordova application.

Taking photos with a phonegap/cordova application and uploading them to the server.

Last days I’ve working in a phonegap/cordova project. The main purpose of the project was taking photos with the device’s camera and sending them to the server. It’s a simple apache cordova project using the camera plugin. According to the documentation we can upload pictures with the following javascript code:

navigator.camera.getPicture(onSuccess, onFail, { 
    quality: 100,
    destinationType: Camera.DestinationType.DATA_URL
});

function onSuccess(imageData) {
    // here we can upload imageData to the server
}

function onFail(message) {
    alert('Failed because: ' + message);
}

As we can see we our plugin retrieves a base64 encoded version of our image and we can send it to the server, using jQuery for example

function onSuccess(imageData) {
  $.post( "upload.php", {data: imageData}, function(data) {
    alert("Image uploaded!");
  });
}

Our server side is trivial. We only need to read the request variable ‘data’, perform a base64 decode and we have our binary picture ready to be saved.

I test it with an old android smartphone (with a not very good camera) and it’s works, but when I tried to use it with a better android phone (with 8mpx camera) it hangs on and it didn’t work. After property reading the documentation I realized that it isn’t the better way to upload files to the server. It only works if the image file is small. Base64 increases the size of the image and our device can have problems handling the memory. This way Is also slow as hell.

The best way (the way that works, indeed) is, instead of sending the base64 files to the server, to save them into a device’s temporary folder and send them using the file transfer plugin.

var pictureSource;   // picture source
var destinationType; // sets the format of returned value

document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {
    pictureSource = navigator.camera.PictureSourceType;
    destinationType = navigator.camera.DestinationType;
}

function clearCache() {
    navigator.camera.cleanup();
}

var retries = 0;
function onCapturePhoto(fileURI) {
    var win = function (r) {
        clearCache();
        retries = 0;
        alert('Done!');
    }

    var fail = function (error) {
        if (retries == 0) {
            retries ++
            setTimeout(function() {
                onCapturePhoto(fileURI)
            }, 1000)
        } else {
            retries = 0;
            clearCache();
            alert('Ups. Something wrong happens!');
        }
    }

    var options = new FileUploadOptions();
    options.fileKey = "file";
    options.fileName = fileURI.substr(fileURI.lastIndexOf('/') + 1);
    options.mimeType = "image/jpeg";
    options.params = {}; // if we need to send parameters to the server request
    var ft = new FileTransfer();
    ft.upload(fileURI, encodeURI("http://host/upload"), win, fail, options);
}

function capturePhoto() {
    navigator.camera.getPicture(onCapturePhoto, onFail, {
        quality: 100,
        destinationType: destinationType.FILE_URI
    });
}

function onFail(message) {
    alert('Failed because: ' + message);
}

I just realized that sometimes it fails. It looks like a bug of cordova plugin (I cannot assert it), because of that, if you read my code, you can see that if fp.upload fails I retry it (only once). With this little hack it works like a charm (and it’s also fast).

The server part is pretty straightforward. We only need to handle uploaded files. Here a minimalistic example with php

<?php
move_uploaded_file($_FILES["file"]["tmp_name"], '/path/to/file');

And that’s all. We can easily upload our photos from our smartphone using phonegap/cordova.

You can read more articles about cordova here.

Multiple Phonegap Push Notifications in the Android’s status bar

Last month I worked within an Android project using Phonegap, jQuery Mobile and Push Notifications. I also wrote one post explaining how to use PHP to send the server side’s part of the push notifications. Today I want to show one small hack, that I’ve done to change the default behaviour of push notifications. Let me explain it a little bit:

When you use the Push Plugin “out of the box” you will see one message in your Android’s status bar everytime we send one push notification (and you application isn’t running at this moment). If you click on the notification you application will start and you can handle this notification. But if you send more than one notifications to the device, only the last one will be shown on the status bar. This behaviour can be suitable for most situations, but within my application I wanted to see all the notifications in the status bar until I click on one (then all must disappear). If we want to do that we need to hack a little bit our Phonegap application. Let me show you what I’ve done.

Basically we need to change com.plugin.gcm.GCMIntentService file. If we open this Java file we can see that there’s one constant called: NOTIFICATION_ID and a public function called createNotification with something like that:

public static final int NOTIFICATION_ID = 237;
...

public void createNotification(Context context, Bundle extras)
{
    ...
    mNotificationManager.notify((String) appName, NOTIFICATION_ID, mBuilder.build());

}

I’m not a Java expert, but I notice that if I change this function to:

public static final int NOTIFICATION_ID = 237;
public static int MY_NOTIFICATION_ID = 237;
...

public void createNotification(Context context, Bundle extras)
{
    ...
    MY_NOTIFICATION_ID++;
    mNotificationManager.notify((String) appName, MY_NOTIFICATION_ID, mBuilder.build());

}

Now my android device will show multiple notifications, exactly as I need.

If we need to handle properly the way that our notifications are cancelled we also need to modify the public function cancelNotification

public static void cancelNotification(Context context)
{
    NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    //mNotificationManager.cancel((String)getAppName(context), MY_NOTIFICATION_ID);
    mNotificationManager.cancelAll();
    MY_NOTIFICATION_ID = NOTIFICATION_ID;
}

And that’s all. Multiple notifications as I needed.

Sending Android Push Notifications from PHP to phonegap applications

Last days I’ve been working within a Phonegap project for Android devices using Push Notifications. The idea is simple. We need to use the Push Notification Plugin for Android. First we need to register the Google Cloud Messaging for Android service at Google’s console, and then we can send Push notifications to our Android device.

The Push Notification plugin provides a simple example to send notifications using Ruby. Normally my backend is built with PHP (and sometimes Python) so instead of using the ruby script we are going to build a simple PHP script to send Push Notifications.

The script is very simple

<?php
$apiKey = "myApiKey";
$regId = "device reg ID";

$pusher = new AndroidPusher\Pusher($apiKey);
$pusher->notify($regId, "Hola");

print_r($pusher->getOutputAsArray());

And the whole library you can see here:

<?php
namespace AndroidPusher;

class Pusher
{
    const GOOGLE_GCM_URL = 'https://android.googleapis.com/gcm/send';

    private $apiKey;
    private $proxy;
    private $output;

    public function __construct($apiKey, $proxy = null)
    {
        $this->apiKey = $apiKey;
        $this->proxy  = $proxy;
    }

    /**
     * @param string|array $regIds
     * @param string $data
     * @throws \Exception
     */
    public function notify($regIds, $data)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, self::GOOGLE_GCM_URL);
        if (!is_null($this->proxy)) {
            curl_setopt($ch, CURLOPT_PROXY, $this->proxy);
        }
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getHeaders());
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $this->getPostFields($regIds, $data));

        $result = curl_exec($ch);
        if ($result === false) {
            throw new \Exception(curl_error($ch));
        }

        curl_close($ch);

        $this->output = $result;
    }

    /**
     * @return array
     */
    public function getOutputAsArray()
    {
        return json_decode($this->output, true);
    }

    /**
     * @return object
     */
    public function getOutputAsObject()
    {
        return json_decode($this->output);
    }

    private function getHeaders()
    {
        return [
            'Authorization: key=' . $this->apiKey,
            'Content-Type: application/json'
        ];
    }

    private function getPostFields($regIds, $data)
    {
        $fields = [
            'registration_ids' => is_string($regIds) ? [$regIds] : $regIds,
            'data'             => is_string($data) ? ['message' => $data] : $data,
        ];

        return json_encode($fields, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
    }
}

Maybe we could improve the library with a parser of google’s ouptuput, basically because we need to handle this output to notice if the user has uninstalled the app (and we need the remove his reg-id from our database), but at least now it cover all my needs. You can see the code at github