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