Thursday 18 October 2012

Installing and Configuring Kannel OpenSMPPBox


Similar to SMSBoxOpenSMPPBox is a type of box that runs on top of an existing Kannel installation.  However this is a special type of box that will listen to incoming SMPP Connections to allow messages to be sent to and from SMPP Clients allowing you to act as an SMPP Provider.

In this post I will provide basic configuration for installing the additional OpenSMPPBox on top of an existing Kannel Installation. You can find how to install Kannel in my previous post, you must however install this on top of the latest version of kannel, found here.

The user guide for Kannel can be found at http://www.kannel.com/userguide.shtml and the user guide for OpenSMPPBox can be found at http://www.scribd.com/doc/89943592/Opensmpp-Userguide .

1. With Kannel 1.5.0 Installed and working, download the OpenSMPPBox source code, available from multiple sources:

https://redmine.kannel.org/projects/smppbox/repository
or 
https://github.com/pruiz/kannel-opensmppbox

The easiest step is to download the zip file from the above github url:

wget https://github.com/pruiz/kannel-opensmppbox/zipball/master

2. Extract the archive 


unzip master 

3. Change to the directory to compile


cd pruiz-kannel-opensmppbox-b6712e2/

Use the following steps to install:


./configure --prefix=/usr/local/kannel --with-kannel-dir=/usr/local/kannel  (specify Kannel base install directory)

NOTE: [ If you receive error like:


/root/pruiz-kannel-opensmppbox-b6712e2/gw/box-dlr.c:178: undefined reference to `dbpool_destroy'
collect2: ld returned 1 exit status
make[2]: *** [opensmppbox] Error 1

you will need to change to the source directory for your base Kannel installation and re-compile with database support 

./configure --with-mysql or --with-pgsql 
make
make install


Then continue in the OpenSMPPBox directory:

cd pruiz-kannel-opensmppbox-b6712e2/


./configure --prefix=/usr/local/kannel --with-kannel-dir=/usr/local/kannel  (specify Kannel base install directory)
make
make install

You'll notice a new file created in usr/local/kannel/sbin/opensmppbox 

4. Change to the kannel directory

cd /usr/local/kannel

Create a new opensmppbox.conf file with the following content, this is basic without database support. Full parameter list can be found in the user guide. :


group = core
dlr-storage = internal

group = opensmppbox
opensmppbox-id = SMPP
opensmppbox-port = 2775
bearerbox-host = 127.0.0.1
bearerbox-port = 13001
our-system-id = SMPP
smpp-logins = /usr/local/kannel/smpplogins.txt
use-systemid-as-smsboxid = true
route-to-smsc = smsc1 (the smsc you want to send to in kannel.conf, could be your own http smsc)
log-file = /usr/local/kannel/logs/smppbox.log

4. Create an smpplogin.txt file in the specified path above. This will define who can connect over SMPP, refer to the guide for more details. A sample line could look like:

testname testpassword smsc1 *.*.*.* 

This can contain an endless list of entires.

5. Ensure kannel base installation is running. Now run OpenSMPPBox using the following commands:

/usr/local/kannel/sbin/opensmppbox  opensmppbox.conf 

If OpenSMPPBox is running correctly and ready to accept connections the log file will show the following:


 INFO: Waiting for SMPP connections on port 2775.

You box is now ready to accept connections on port 2755 from users defined in smpplogin.txt file.

For further reference and exploration, the user guide for Kannel can be found at http://www.kannel.com/userguide.shtml and the user guide for OpenSMPPBox can be found at http://www.scribd.com/doc/89943592/Opensmpp-Userguide .

Monday 15 October 2012

Using Google Street View in Android Applications

Street View is a nice feature Google have created for us to see street level images of our current position or specified location... you can find more information on getting your current location in this post.

Using street view in android applications can be accomplished in a number of ways depending on the final objective.  The two ways I'm going to explain here is 1. Using Street View Intent and 2. Using Street View inside an embedded Web View.  There are benefits to each of these methods but each will have their drawbacks and limitations when you start to explore further.

1. Using Street View Intent

The street view intent is the quick and easy way to see a location at street level with the minimal code and effort. The following intent will launch the street view application installed on the android device and go the the desired location:
In my specific code I add try / catch around the startActivity() in case there is errors loading the street view application. 
                Uri streetViewUri = Uri.parse(
                        "google.streetview:cbll=" + geoLocation + "&cbp=1,90,,0,1.0&mz=20");
                Intent streetViewIntent = new Intent(Intent.ACTION_VIEW, streetViewUri);
                startActivity(streetViewIntent);

where "geoLocation" is the longitude and latitude co-ordinates of the location you would like to see such as "51.5271124,-0.1337308" .

Full details on all parameters required and what can be included in the intent are available at Invoking Google Applications on Android Devices .

One drawback of using this may be that the street view application is not installed on the device by default so either installing it or detecting it with PackageManager / queryIntentActivites() will help resolve.

                 try {
                    startActivity(streetViewIntent);
                } catch (Exception e) {
                    Toast toast = Toast.makeText(getApplicationContext(), "Error Loading StreetView ",
                            Toast.LENGTH_SHORT);
                    toast.show();
                }

There will be cases where there is no street view for the specified location and an empty white page will be seen so you may have to do some magic with Google's api to check if street view is available for that location first.

If you want to start doing any thing fancy using street view, maybe displaying a marker for the location or even multiple markers with a custom image. This way you have to then start looking at using a web view and Google Street View Service ..... 

2. Using Street View inside an embedded Web View.

Google Street View Service contains full documentation on how to customise your street view in any webview with custom html code. In this explanation I am going to explain how to get set up for this in an android application.

Firstly create a standard activity with basic xml layout, including a webview

Configure settings for the webview as follows:

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDomStorageEnabled(true);

You will then need to create some custom html code that will be loaded into this view. You can come up with many different combinations and examples from reading the Street View Api  however below is a basic example that will load street view at your current or specified location with a custom marker for a place:


String htmlCode = "

<html>
  <head>
    <meta charset="utf-8">
    <title>Google Maps JavaScript API v3 Example: Street View Layer</title>
    <link href="/maps/documentation/javascript/examples/default.css" rel="stylesheet">
    <script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
    <script>
      var panorama;

      function initialize() {

        var myLocation = new google.maps.LatLng(51.5271124,-0.1337308);
        var panoramaOptions = {
          position: myLocation,
        };

        panorama = new  google.maps.StreetViewPanorama(document.getElementById('pano'),panoramaOptions);


        var stationMarker = new google.maps.MarkerImage('http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Underground.svg/500px-Underground.svg.png');

        var station = new google.maps.Marker({
        position: new google.maps.LatLng(51.5274683,-0.1345183),
        map: panorama,
        icon: stationMarker,
        title: 'Euston Station',
       });
      }

    </script>

  </head>
  <body onload="initialize()">
    <div id="pano" style='width:100%; height:100%; padding:0; margin:0; -webkit-user-modify: read-write-plaintext-only; -webkit-tap-highlight-color:rgba(0,0,0,0)'></div>
  </body>
</html>  "

The above code firstly ensure we are using street view and positioned at "myLocation". Then we create a custom marker name "stationMarker" using an image from a custom url and display this at the new postion (51.5274683,-0.1345183).


The bottom code is some custom styling to make the street view look more appealing on the android device. 


You can also play around with the string and replace the long / lat ordinates with your own location like below:


"        var myLocation = new google.maps.LatLng(" + myLocation +");"


Any manipulation and substitution can be done here to get custom data into the html string. In my own example app I plot multiple overlays just by replicating the "stationMarker" section and setting new positions.  


The final pieces of code required are:


        webView.loadData(htmlCode, "text/html", "UTF-8");

        webView.setWebViewClient(new MyWebViewClient()); 

The MyWebViewClient class will allow you to load the html into your own webview rather than launching the default browser or selection dialog:



    private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    }

Using street view in a webview by specifying custom html code is more flexible in what you can do and show, however things can get very complex if your not too familiar with the simple html / java script coding. Even the tiniest mistake or missing character will cause the html code to break. 

To help: One thing that I found easier is test the html string in a live online code tester such as http://htmledit.squarefree.com/ first, this will save modifying your app and running the android application over and over again!

Thursday 2 August 2012

Android getting the best user Location

Getting the most accurate user location on an android device can turn out to be a harder task then you initially wanted. From looking at the developer docs available at http://developer.android.com/reference/android/location/LocationManager.html there are a number of different methods available, so knowing which is best way for your application can be troublesome.

If you just want a rough location for you application you can attempt to get the "last known location". You can query any of the three providers using the following code to get the last fix they had:

Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

"GPS_PROVIDER" can be replaced with "NETWORK_PROVIDER" or "PASSIVE_PROVIDER" depending on which provider you wish to query.

This can be annoying if you query just one provider and there is no location you then have to query each again. So from this post http://stackoverflow.com/questions/4735942/android-get-location-only-one-time I modified and used the following:

This will loop over the location providers backwards, so that if there is no location from gps it will try network etc.....

    public static Location getLastKnownLocation(Context context) {
        LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        List<String> providers = lm.getProviders(true);


        /* Loop over the array backwards, and if you get an accurate location, then break out the loop*/
        Location l = null;


        for (int i = providers.size() - 1; i >= 0; i--) {
            l = lm.getLastKnownLocation(providers.get(i));
            if (l != null) break;
        }
        return l;
    }

The issue with using getLastKnowLocation is that it might be old or not very accurate. If you want something more accurate for you app you need to write something a bit more complex which involves getting you application to listen to location changes.

To do this I started from this post http://stackoverflow.com/questions/3145089/what-is-the-simplest-and-most-robust-way-to-get-the-users-current-location-in-a/3145655#3145655 and modified slightly for my use. The class created basically follows these steps:

1. Check which providers are enabled. Users may have disabled gps from settings or cell signal / triangulation is not available.
2. Go through the available providers in the following order: gps, cell, passive and start the location listeners for a set time. If a location if not found before the time runs out then move to the next provider.
3. Once a location is found then break out from the listener timers and return this location back to the activity.
4. If no location is found from any provider then go back to using last known location, but what ever is most recent out of the three providers.

Below is the Location class that I use to do all of the above querying, some comments added to explain the process:

public class Location extends Activity {


    private Timer timer1;
    private LocationManager lm;
    private LocationResult locationResult;
    private boolean gps_enabled = false;
    private boolean network_enabled = false;
    private boolean passive_enabled = false;


    public boolean getLocation(Context context, LocationResult result) {
        //use LocationResult callback class to pass location value from Location to the activity.
        locationResult = result;
        if (lm == null)
            lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        // check to see which providers are enabled, and throw exception if not available 
        try {
            gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
        } catch (Exception ex) {
           // gps not available
        }
        try {
            network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        } catch (Exception ex) {
           // cell not available
        }


        try {
            passive_enabled = lm.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
        } catch (Exception ex) {
           // wifi / passive not available
        }


        // if no providers are enabled don't start listeners and return false to the activity
        if (!gps_enabled && !network_enabled && !passive_enabled)
            return false;


        // use gps provider first
        if (gps_enabled) {
            lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
            // start a timer to listen to gps for 25 secs, gps requires longer than the others
            timer1 = new Timer();
            timer1.schedule(new GetLastGPSLocation(), 25000);
            // use network as the second provider
        } else if (network_enabled) {
            lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
           // start a timer to listen to cell triangulation for 15 secs
           timer1 = new Timer();
            timer1.schedule(new GetLastNetworkLocation(), 15000);
            // revert to passive as a last resort
        } else {
            lm.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, locationListenerPassive);
          // start a timer to listen to wifi passive networks for 15 secs
            timer1 = new Timer();
            timer1.schedule(new GetLastPassiveLocation(), 15000);
        }
        return true;
    }


    private final LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location) {
            // we've got a gps fix, cancel timer, listener and return location
            timer1.cancel();
            locationResult.gotLocation(location);
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerGps);
        }


        public void onProviderDisabled(String provider) {
        }


        public void onProviderEnabled(String provider) {
        }


        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    };


    private final LocationListener locationListenerNetwork = new LocationListener() {
        public void onLocationChanged(Location location) {
            // we've got a network fix, cancel timer, listener and return location
            timer1.cancel();
            locationResult.gotLocation(location);
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerNetwork);
        }


        public void onProviderDisabled(String provider) {
        }


        public void onProviderEnabled(String provider) {
        }


        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    };


    private final LocationListener locationListenerPassive = new LocationListener() {
        public void onLocationChanged(Location location) {
            // we've got a passive fix, cancel timer, listener and return location
            timer1.cancel();
            locationResult.gotLocation(location);
            lm.removeUpdates(this);
            lm.removeUpdates(locationListenerPassive);
        }


        public void onProviderDisabled(String provider) {
        }


        public void onProviderEnabled(String provider) {
        }


        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
    };


    private class GetLastGPSLocation extends TimerTask {
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                public void run() {
                    //gps has timed out without a change we should start the next listener
                    lm.removeUpdates(locationListenerGps);
                   if (network_enabled) {
                        lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
                        timer1 = new Timer();
                        timer1.schedule(new GetLastNetworkLocation(), 15000);
                    } else if (passive_enabled) {
                        lm.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, locationListenerPassive);
                        timer1 = new Timer();
                        timer1.schedule(new GetLastPassiveLocation(), 15000);
                    } else {
                        // no more providers enabled, so get most recent
                        returnRecentLocation();
                    }
                }
            });
        }
    }


    private class GetLastNetworkLocation extends TimerTask {
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                public void run() {
                    //network has timed out without a change we should start next listener
                    lm.removeUpdates(locationListenerNetwork);
                   if (passive_enabled) {
                        lm.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, locationListenerPassive);
                        timer1 = new Timer();
                        timer1.schedule(new GetLastPassiveLocation(), 15000);
                    } else {
                        // no more providers enabled, so get most recent
                        returnRecentLocation();
                    }
                }
            });
        }
    }


    private class GetLastPassiveLocation extends TimerTask {
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                public void run() {
                    lm.removeUpdates(locationListenerPassive);
                   //passive has timed out without a change so get most recent
                    returnRecentLocation();
                }
            });
        }
    }


    private void returnRecentLocation() {
        Location net_loc = null, gps_loc = null, passive_loc = null;


        if (gps_enabled)
            gps_loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if (network_enabled)
            net_loc = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        if (passive_enabled)
            passive_loc = lm.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);


        //if there are both values use the latest one
        if (gps_loc != null && net_loc != null) {
            if (gps_loc.getTime() > net_loc.getTime())
                locationResult.gotLocation(gps_loc);
            else
                locationResult.gotLocation(net_loc);
            return;
        }


        if (gps_loc != null) {
            locationResult.gotLocation(gps_loc);
            return;
        }
        if (net_loc != null) {
            locationResult.gotLocation(net_loc);
            return;
        }
        if (passive_loc != null) {
            locationResult.gotLocation(passive_loc);
            return;
        }


        // we couldn't get anything
        locationResult.gotLocation(null);
    }


    public static abstract class LocationResult {
        public abstract void gotLocation(Location location);
    }
}




Below is how I call the class from my activity:

1. First I check in my local database to ensure my location isn't "old". If my local location is less than 5 minutes I don't query the providers and just use this again.
2. If I need to get a new location I use the following:

Location location = new Location();
providerEnabled = location.getLocation(getApplicationContext(), locationResult);


3. The callback:

   private final Location.LocationResult locationResult = new Location.LocationResult() {
        @Override
        public void gotLocation(final Location location) {
            //Got the location. Save location and date to local db to use in application 
       }
    };

Currently there isn't a much simpler way to query to location providers to get an accurate location. People have written various classes and methods to get the most accurate location without affecting device performance too much. I find that the above class works well and is only called when required from the activity so limits to need to ask for hungry gps resources. 


I'm also happy to take on any tweaks or ideas for the above code. 



Thursday 19 July 2012

Developing "Facebook Sidebar Menu" for Android

Sliding sidebar, sliding menu, ribbon menu, animation layout, side view, action menu.....

Many apps are now adapting to the new "side-bar" menu that Facebook has come up with. However many people don't know that this is not a native look / view in the android app. Facebook are actually using some kind of Javascript/HTML/CSS coding to display this view, and only few features such as the "settings" page are actually native android code.

I asked to develop a similar view in an existing android application. I needed to do this in native android / java code, not javascript or any kind of web view that Facebook seem to be using. After searching the web I came across a number of existing projects where users have been attempting to do the same thing. 

The following are example libraries or source codes that can be used within android code to display this side bar menu effect:



https://github.com/korovyansk/android-fb-like-slideout-navigation







Out of the many views and different ways of effectively producing the same look, I ended up starting with the Animation Layout available from the https://github.com/walkingice/gui-sliding-sidebar example above and modifying it to work with my requirements. 

All of the examples above aren't perfect answers to developing this view but if you are looking to write this type of view in native android code then they are a great starting point.


Other android apps using a similar view for examples:
Facebook, Google+, Evernote



Thursday 1 September 2011

Installing and configuring Kannel SMS Gateway


Kannel is an open source SMS and WAP gateway. It's freely available software used by operators, carriers, mobile aggregators and other users to send and receive SMS messages and mobile content.
Further details of Kannel can be found at Kannel's Website . There are many different ways to implement Kannel so it depends what your need is.

A lot of users setup Kannel to connect to HTTP and SMPP providers in order to send SMS messages. In this post I will show how to install, configure and send your first SMS using the gateway

For reference, the user guide can be found at http://www.kannel.com/userguide.shtml

1. Firstly ensure all software requirements are installed and then download the source from the Kannel Download site.

wget http://www.kannel.com/download/1.4.3/gateway-1.4.3.tar.gz

2. Extract the archive file to directory.

tar -xzvf gateway-1.4.3.tar.gz

3. Change to the directory to compile

cd gateway-1.4.3

We want to install to /usr/local so we complete the following steps


./configure  ---prefix=/usr/local/kannel
make
make install

4. Change to the new directory that has been created

cd /usr/local/kannel

5. All the files for kannel are now located in this directory. In order to get Kannel up and running quickly we need to create a new config file.

Refer the the user guide for all parameters available, however the following configuration file is useful for setting up a main 'bearerbox' with an 'smsbox' in which we can send messages to an SMPP carrier.
Substitute fields highlighted with you own details. Example files can also be found in the original source files.

vi kannel.conf

group = core
dlr-storage = internal
admin-port = 13000
admin-password = password
status-password = password
admin-allow-ip = ''
smsbox-port = 13001
log-level = 0
log-file = "/usr/local/kannel/logs/kannel.log"
box-allow-ip = "127.0.0.1"

group = smsbox
smsbox-id = BOX1
bearerbox-host = 127.0.0.1
sendsms-port = 13013
log-file = "usr/local/kannel/logs/smsbox.log"
log-level = 0
access-log = "usr/local/kannel/logs/access.log"

group = sendsms-user
username = user1
password = password1

group = smsc
smsc = smpp
smsc-id = SMSC1
host = 111.111.111.111
port = 12345
smsc-username = my smsc details
smsc-password = my smsc password
address-range = ""
system-type = ""
transceiver-mode = true


6. To start the gateway run the following command

/usr/local/kannel/sbin/bearerbox kannel.conf

ensure kannel.conf is replaced with the full path to you configuration file.

Start the sms box also

/usr/local/kannel/sbin/smsbox kannel.conf

8. You can view the status of the gateway by typing the following in a browser

localhost:13000/status?password=password

7. To test the functionality of the gateway, in a browser send a new sms message to the gateway using the following url:

http://localhost:13013/cgi-bin/sendsms?username=user1&password=password1&to=0123456789&from=4444&text=testmessage


 For further reference, the user guide can be found at http://www.kannel.com/userguide.shtml