Thursday, 18 October 2012

Installing and Configuring Kannel OpenSMPPBox


Similar to SMSBox, OpenSMPPBox 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

Wednesday, 31 August 2011

Installing Java JDK Linux Simple Steps

There are many of ways to install the Java Development kit onto Linux operating systems. This includes installing from yum repositories. However some of these versions of java my have components missing.

The following is one of the most simplest way and ensure all of the JDK components are installed:

1. Download the compressed binary from the Java site. For example

wget http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-x64.tar.gz


2. Once downloaded run tar -xzvf on the file

3. mv the directory to /usr/local/

4. Append a reference to the directory your local profile

e.g.  vi ~/.bash_profile

add the following:

export JAVA_HOME=/usr/local/jdk1.7.0
export PATH=$PATH:/usr/local/jdk1.7.0/bin


3. Log out and back in again. Run java -version to confirm installation

Wednesday, 19 January 2011

Overview of technologies used so far

There are a number of application that work well on there own and are great for the purpose they are designed for. However everything links together some how and understanding how applications work on their own is only the start of things.

Whilst I have been learning about a number new applications, I have also been looking at how applications can work with each other to give an end to end feel of how certain Internet businesses or back end technologies really work.

There are hundreds or some times thousands of different way in which applications can link with each other within a business or over the Internet. Whether it be using a different storage database at the back end or it be writing a front end client in a different language, I have been documenting a number of working solutions already in my blog http://vidorsolutions.com

Some of the technologies I have learnt and experimented successfully with are as follows:

1. Grails application deployed into Glassfish application server, using PostgreSQL as a database. Then using tools such as PGAdmin to connect to my databases locally on my machine.

2. Ejabberd Instant Messaging server installed on a server instance in the cloud, configured to use postgreSQL as a data source. Then using PSI Desktop chat client to connect with Ejabberd account. Tools such as PGAdmin can also be used here to view the back end database, tables and structure.

To further extend this learning experience I then created a basic Android XMPP client application that could also connect to the Ejabberd Server instance.

A lot of different technologies going on here and a lot to learn about the applications alone if you've never used any of these. However once you see how all these work together you get a greater appreciation of how the end to end user experience works.

Remember for some instructions on how to use these check out my previous posts
http://vidorsolutions.com

Tuesday, 18 January 2011

Writing a basic XMPP/Jabber chat application for Android

There are many chat applications for android open source that you can use to connect to an XMPP or Jabber server instance. This could be you own server such as Ejabberd or one such as Facebook / yahoo etc.

I have started to look at how to write a basic android XMPP client that I can use to connect to my Ejabberd server. I have pulled a number of sources available to gain knowledge in android, initially stating with Professional Android 2 Application Development.
There are a number other sample applications in this book that will aid in learning the android programming language.

However to understand how to create an XMPP application I started looking at Smack, an XMPP client library.
This is the library you will need to import and refer to when writing your android XMPP client. Google no longer has it's own native XMPP library.

Smack has be heavily patched to work with android devices, re-badged asmack, library downloads are available from http://code.google.com/p/asmack/
.

Once you have this imported into your project workspace you can now write a basic chat client. I used the following example code for my first point of call:

http://davanum.wordpress.com/2008/12/29/updated-xmpp-client-for-android/

After understanding what each bit of code here meant I was able to write my own similar app. Initially the original sample I'm going to show is very similar to the link above, so I cannot take full credit, I'm just sharing resources.
I have now heavily added to the app with shared preferences custom views and content providers etc, I may share the source in later posts.

You will need to write xml layouts to go with the code but the code behind the main application is as follows:

notice the org.jivesoftware.smack imports:

ChatClient.java:

import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.util.StringUtils;

public class ChatClient extends Activity {
   
    private ArrayList<String> messages = new ArrayList();
    private Handler handler = new Handler();
    private xmppsettings settings;
    private EditText recipient;
    private EditText text;
    private ListView list;
    private XMPPConnection connection;
   
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        recipient = (EditText) this.findViewById(R.id.recipient);
        text = (EditText) this.findViewById(R.id.text);
        list = (ListView) this.findViewById(R.id.messageList);
        setListAdapter();
       
        //Window for getting settings
        settings = new xmppsettings(this);
       
        //Listener for chat message
        Button send = (Button) this.findViewById(R.id.send);
        send.setOnClickListener(new View.OnClickListener() {
           
            @Override
            public void onClick(View v) {
                String to = recipient.getText().toString();
                String text1 = text.getText().toString();
               
                Message msg = new Message(to, Message.Type.chat);
                msg.setBody(text1);
                connection.sendPacket(msg);
                messages.add(connection.getUser() + ":");
                messages.add(text1);
                setListAdapter();               
            }
        });       
    }
   
    //Called by settings when connection is established
   
    public void setConnection (XMPPConnection connection) {
        this.connection = connection;
        if (connection != null) {
            //Packet listener to get messages sent to logged in user
            PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
            connection.addPacketListener(new PacketListener() {
                public void processPacket(Packet packet) {
                    Message message = (Message) packet;
                    if (message.getBody() != null) {
                        String fromName = StringUtils.parseBareAddress(message.getFrom());
                        messages.add(fromName + ":");
                        messages.add(message.getBody());
                        handler.post(new Runnable(){
                            public void run() {
                                setListAdapter();
                            }
                        });
                    }
                }
            }, filter);
        }
    }
   
    private void setListAdapter() {
        ArrayAdapter<String> adapter = new ArrayAdapter<String>
            (this, R.layout.list, messages);
        list.setAdapter(adapter);
    }
   
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }
   
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
        case R.id.settings:
            settings.show();
            return true;
        case R.id.quit:
            finish();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
   
    }
}




xmppsettings.java: (I have made this a menu item)


import android.app.Dialog;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;

//settings get input and then connection is established

public class xmppsettings extends Dialog implements android.view.View.OnClickListener  {
    private ChatClient chatClient;
   
    public xmppsettings(ChatClient chatClient){
        super(chatClient);
        this.chatClient = chatClient;
    }
   
    protected void onStart() {
        super.onStart();
        setContentView(R.layout.settings);
        setTitle("Connection Settings");
        Button ok = (Button) findViewById(R.id.ok);
        ok.setOnClickListener(this);
    }
   
    public void onClick(View v) {
        String host = getText(R.id.host);
        String port = getText(R.id.port);
        String service = getText(R.id.service);
        String username = getText(R.id.userid);
        String password = getText(R.id.password);
       
        // Create connection
       
        ConnectionConfiguration connectionConfig =
            new ConnectionConfiguration(host, Integer.parseInt(port), service);
        XMPPConnection connection = new XMPPConnection(connectionConfig);
       
        try {
            connection.connect();
        } catch (XMPPException ex) {
            chatClient.setConnection(null);
        }
        try {
            connection.login(username, password);
           
            // Set status to online / available
            Presence presence = new Presence(Presence.Type.available);
            connection.sendPacket(presence);
            chatClient.setConnection(connection);
        } catch (XMPPException ex) {
            chatClient.setConnection(null);
        }
        dismiss();
    }
   
    private String getText(int id) {
        EditText widget = (EditText) this.findViewById(id);
        return widget.getText().toString();
    }
}



After these initial files I then when one to create a shared preferences which will save the settings that a user inputs.
Also then when on to write a separate array adapter within the chatclient.java class which then aided in the creation of a content provider to store the messages in a db.

This is the application at the most basic, but will get you started in developing any high end chat app.

Monday, 17 January 2011

Adding Modules to Ejabberd Installation, Linux Statsdx

Ejabberd XMPP messaging server comes with a few modules with the initial installation. Some of these are useful however you can extend the features of the server a lot more by using additional modules.

There are a number of modules that can be downloaded from https://svn.process-one.net/ejabberd-modules/  , also there are a number of custom modules on the net, or you can even write your own; if you know erlang.

For example if you wanted some more useful user statistics for your server, rather than just the basic Statistics tab from the web admin page, that just gives you the following details:

Registered Users
Online Users
Outgoing s2s Connections
Outgoing s2s Servers

Why not install the statsdx module that gives you a lot more information.

All modules are installed into the /opt/ejabberd-2.1.5/lib/ejabberd-2.1.5/ebin directory however each module will have it's own set-up requirements written in the README.txt available in each module trunk directory.

To install statsdx :

change to /opt/ejabberd-2.1.5/lib/ejabberd-2.1.5 directory

run svn co https://svn.process-one.net/ejabberd-modules/ to get the latest modules

cd ejabberd-modules/mod_statsdx/trunk


run ./build.sh

copy generated beam file(s) from trunk/ebin directory to
/opt/ejabberd-2.1.5/lib/ejabberd-2.1.5/ebin


edit /opt/ejabberd-2.1.5/conf/ejabberd.cfg:

Scroll down to the section headed modules, this is where you enable each module on your server.


Append the following the the end of the enable modules:

{mod_statsdx, [{hooks, true}]}
(as instructed in the README.txt available with the module src files)

run ./opt/ejabberd-2.1.5/bin/ejabberdctl restart to restart the server instance

Browse to http://localhost:5280/admin/ to see you have more stats available under the Statistics DX tab, such as:

Registered Users
Roster Details
User details
MUC information
Sessions per client, OS, connection type, Languages

There are a lot more modules you can use to extend your basic Ejabberd installation, depending on what you want to do or know.

Sunday, 2 January 2011

Installing GlassFish application Server cluster on Linux

GlassFish is a Java EE open source application server. The following instructions demonstrate how to install GlassFish 2.1.1 on any Linux platform.

http://glassfish.java.net/javaee5/build/GlassFish_LB_Cluster.html contains full information on downloading and installing glassfish clusters and load balancer plugin.

I am going to explain the steps I took to install glassfish clusters as I didn't require load balance support.

Please note all commands will require elevated privileges.

1. Download binary installer from http://glassfish.java.net/downloads/v2.1.1-final.html 
save to /opt directory.

2. Ensure JAVA_HOME variable is set to JDK directory

3. Within the /opt directory run java -Xmx256m -jar glassfish-installer-v2.1.1-b31g-linux.jar

INSTALLATION COMPLETE is displayed
This UN-bundles glassfish and installs under a new directory 'glassfish'

4. Change to glassfish directory cd glassfish/


5. Change permissions on lib/ant/bin sub directory using chmod -R +x lib/ant/bin


6. Run lib/ant/bin/ant -f setup-cluster.xml . If successful you get BUILD SUCCESSFUL displayed

7. From /opt/glassfish/bin run ./asadmin start-domain domain1 to start to default domain.
Domain [domain1] is running is displayed

8. Browse to http://localhost:8080/ to confirm Enterprise server is now running.

9. Browse to http://localhost:4848/ to access admin console. Default user name and password are username: admin, password: adminadmin . You can change this from the Application Server settings on the web admin interface page.

The server is now running and now we are going to look at creating a one machine cluster.

10. Change to directory /opt/glassfish/samples/quickstart/clusterjsp

Run /opt/glassfish/bin/asant setup-one-machine-cluster

This does the following:

Creates a cluster with name cluster1
Creates a node-agent with name cluster1-nodeagent
Starts the above
Creates two instances under the cluster

You can change these default values under /opt/glassfish/samples/quickstart/clusterjsp/cluster.properties before running

BUILD SUCCESSFUL is displayed

12. Start the cluster using glassfish/bin/asant start_cluster 

BUILD SUCCESSFUL is displayed again


13. View and confirm node with two instances is setup from http://localhost:4848/  select node agent then cluster1-nodeagent on the left pane




If you need to test the two instances you can simply deploy an application in glassfish and browse to the instance using http:localhost:PORT/appname 

If you continue reading the link below you can learn how to use the load balancer plugin and set up Sun Java System Web Server for this.


http://glassfish.java.net/javaee5/build/GlassFish_LB_Cluster.html


http://docs.sun.com/app/docs/doc/821-0186/aboaa?a=view - also a useful link for getting started

Using PostgreSQL database driver within your Grails applications

By default Grails uses an in-memory database that stores data for your apps within volatile RAM. You can leave this as it is but most programmers like to know that their database is more peristent.

In order to use your own data sources you need to be looking at the DataSource.groovy file within /grails-app/conf directory. Here you specify the database that your application will use.

There are three sections within this file so you can specify different data sources for different purposes: production, test and development. In the example I will demonstrate using a different PostgreSQL database for each purpose however you can use three entirely different products if you wish.


Before you start configuring anything in the DataSource.groovy file ensure that you have completed the following:

1. Ensure PostgreSQL is installed


2. Download PostgreSQL JDBC driver and save it into your applications lib folder.

3. Ensure you have a PostgreSQL user to use for the database connection. If you do not wish to use default user, create new user with createuser command.


4. Create the relevant databases in PostgreSQL that your grails application will use. This can be done simply though PGAdmin or using createdb commands as the PostgreSQL user.

5. Return to the Datasource.groovy config file and use the following settings:

dataSource {
        pooled = true
        driverClassName = "org.postgresql.Driver"
        username = "postgres"
        password = "password"
}
hibernate {
    cache.use_second_level_cache=true
    cache.use_query_cache=true
    cache.provider_class='org.hibernate.cache.EhCacheProvider
'
}
// environment specific settings
environments {
        development {
                dataSource {
                        dbCreate = "update" // one of 'create', 'create-drop','update'
                        url = "jdbc:postgresql://localhost:5432/devdb"
                }
        }
        test {
                dataSource {
                        dbCreate = "update"

                        url = "jdbc:postgresql://localhost:5432/testdb"
                }
        }
        production {
                dataSource {
                        dbCreate = "update"
                        url = "jdbc:postgresql://localhost:5432/proddb"
                }
        }
}


Please note that the following are examples of the different database purposes:

  • Development db is when you use grails run-app to launch you application
  • Test db is when you run grails unit and integration tests
  • Production db is used when you upload the application to an app server such as glassfish etc.
Now run you application in whatever environment you wish and view data in your own PostgreSQL databases. If you not a SQL savey then learn how to use PGAdmin to view tables and database structure.