Zachary Powell

Software Programmer

Zachary Powell

Download my CV Send me a Message

Happy Christmas from Me!

Zachary Powell - 23/12/2015

Well, this will be the last post I make before Christmas, so I wanted to take a second out of the normal posting to wish you all a Merry Christmas and a Happy New Year.

Personally, I have already got my main present from my wife, because she knows me so well that I can never wait until the day for presents!

2015-12-17 17.24.38

So I know what I will be playing with non stop for a good while! I will at some point in the next few weeks also post a device review. I have often been a fan of Samsung hardware, but not their software (normally opting to Root past devices). Can the newest iteration change this?

I must admit this year I have not been feeling very ‘Christmasy’. There have been far less lights on houses and people just don’t seem to have many decorations up in general. I guess it has alot to do with the weather as well, it has been so mild it has not felt like Christmas at all!

In fact, so much so. that this year I have not even bothered to put any decorations up, although I do have a new Christmas tree!!

2015-12-17 17.25.08

Using SQlite on iOS

Zachary Powell - 21/12/2015

We have in the past looked at using SQlite on Android, today we shall look at doing the same under iOS.

Before we start, I will assume that you either have a pre-existing app you want to use SQLite with, or you have just created a new project within Xcode. Either way will work well.

SQLite on iOS

Before we start with the actual implementation, we need to import the SQLite 3 library into our project. This will allow us to then use it within our database handler class.

First in the project navigator, click on your projects name. Make sure the general tab is selected under “Linked Frameworks and Libraries” section, then click on the small plus icon.

Screen Shot 2015-12-17 at 18.09.28

This will bring up a window that allows you to select frameworks and libraries to be added. Here, we need to search for sqlite and then select the item called “libsqlite3.tdb”. Finally click on ‘add’ to actually add the library to your project.

Screen Shot 2015-12-17 at 18.14.30

Objective C Object Class

Just as with the Android example, lets first make a simple Object class that we can use to save and get the data from the database.

BuildingLocation.h

#import <Foundation/Foundation.h>

@interface BuildingLocation : NSObject {
    NSString *_name;
    NSString *_description;
    float  _Long;
    float  _Lat;
}

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *description;
@property (nonatomic) float Long;
@property (nonatomic) float Lat;

- (id)init:(NSString *)name description:(NSString *)description Long:(float)Long Lat:(float)Lat;

@end

BuildingLocation.m

#import "BuildingLocation.h"

@implementation BuildingLocation

@synthesize name = _name;
@synthesize description = _description;
@synthesize Long = _Long;
@synthesize Lat =_Lat;

-(id)init:(NSString *)name description:(NSString *)description Long:(float)Long Lat:(float)Lat {
    if ((self = [super init])) {
        self.name = name;
        self.description = description;
        self.Long = Long;
        self.Lat = Lat;
    }
    return self;
}

@end

The above header and main file are much the same as our Android version. We set up a couple of variables to store a location, and then set up an init function. Unlike the Java Android version, we did not set up getter and setter methods as this time around we will access the variables directly.

Database Handler

Now we shall set up the Header and main file for the actual database handler.

DBHandler.h

#import <Foundation/Foundation.h>
#import "BuildingLocation.h"
#import <sqlite3.h>
#import <CoreLocation/CoreLocation.h>


@interface DBHelper : NSObject
{
    NSString *databasePath;
}

+(DBHelper*)getSharedInstance;

-(BOOL)createDB;

-(BOOL) clearLocations;

-(BOOL) insertLocation:(BuildingLocation*)location;

-(NSArray*) getAllLocations;

@end

Here we just define the methods that we will be creating within the Main file. Also make sure to include the header file for your object class and the SQlite library.

DBHandler.m

#import "DBHelper.h"
static DBHelper *sharedInstance = nil;
static sqlite3 *database = nil;
static sqlite3_stmt *statement = nil;

@implementation DBHelper

+(DBHelper*)getSharedInstance{
    if (!sharedInstance) {
        sharedInstance = [[super allocWithZone:NULL]init];
        [sharedInstance createDB];
    }
    return sharedInstance;
}


-(BOOL)createDB{
    NSString *docsDir;
    NSArray *dirPaths;
    // Get the documents directory
    dirPaths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    docsDir = dirPaths[0];
    // Build the path to the database file
    databasePath = [[NSString alloc] initWithString:
                    [docsDir stringByAppendingPathComponent: @"town.db"]];
    BOOL isSuccess = YES;
    NSFileManager *filemgr = [NSFileManager defaultManager];
    if ([filemgr fileExistsAtPath: databasePath ] == NO)
    {
        const char *dbpath = [databasePath UTF8String];
        if (sqlite3_open(dbpath, &database) == SQLITE_OK)
        {
            char *errMsg;
            const char *sql_stmt = "CREATE TABLE if not exists LocationInfo (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, Name TEXT, Description TEXT, Longitude TEXT, Lat TEXT)";
            
            if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
            {
                isSuccess = NO;
                NSLog(@"Failed to create table");
            }
            sqlite3_close(database);
            return  isSuccess;
        }
        else {
            isSuccess = NO;
            NSLog(@"Failed to open/create database");
        }
    }
    return isSuccess;
}

-(BOOL) clearLocations{
    const char *dbpath = [databasePath UTF8String];
    if (sqlite3_open(dbpath, &database) == SQLITE_OK)
    {
        NSString *insertSQL = @"DELETE FROM LocationInfo";
        const char *insert_stmt = [insertSQL UTF8String];
        sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
        if (sqlite3_step(statement) == SQLITE_DONE)
        {
            return YES;
        }
        else {
            return NO;
        }
    }
    return NO;
}

-(BOOL) insertLocation:(BuildingLocation*)location{
    const char *dbpath = [databasePath UTF8String];
    if (sqlite3_open(dbpath, &database) == SQLITE_OK)
    {
     
        NSNumber *longNumber = [NSNumber numberWithFloat:location.Long];
        NSNumber *latNumber = [NSNumber numberWithFloat:location.Lat];

        NSString *insertSQL = [NSString stringWithFormat:@"INSERT INTO LocationInfo (Name, Description, Longitude, Lat) values (\"%@\",\"%@\", \"%@\", \"%@\")",location.name, location.description, [longNumber stringValue], [latNumber stringValue]];
        const char *insert_stmt = [insertSQL UTF8String];
        sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
        if (sqlite3_step(statement) == SQLITE_DONE)
        {
            return YES;
        }
        else {
            NSLog(@"Error while updating. '%s'", sqlite3_errmsg(database));
            return NO;
        }
    }
    return NO;
}

-(NSArray*) getAllLocations{
    const char *dbpath = [databasePath UTF8String];
    NSString *querySQL;
    if (sqlite3_open(dbpath, &database) == SQLITE_OK)
    {
        NSString *querySQL = @"";
        
        querySQL = @"SELECT * FROM LocationInfo";
       
        const char *query_stmt = [querySQL UTF8String];
        NSMutableArray *resultArray = [[NSMutableArray alloc]init];
        if (sqlite3_prepare_v2(database, query_stmt, -1, &statement, NULL) == SQLITE_OK)
        {
            while (sqlite3_step(statement) == SQLITE_ROW)
            {
                BuildingLocation *location = [[BuildingLocation alloc] init];
                
                location.name = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
                location.description = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
                NSNumberFormatter * nFormatter = [[NSNumberFormatter alloc] init];
                [nFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
                NSNumber *numlong = [nFormatter numberFromString:[[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 4)]];
                NSNumber *numlat = [nFormatter numberFromString:[[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 5)]];

                location.Long = [numlong floatValue];
                location.Lat = [numlat floatValue];

                [resultArray addObject:location];
            }
            return resultArray;
        }
    }
    return nil;

}

@end

This is the complete file with the same methods we implemented with Android.

+(DBHelper*)getSharedInstance{
    if (!sharedInstance) {
        sharedInstance = [[super allocWithZone:NULL]init];
        [sharedInstance createDB];
    }
    return sharedInstance;
}

The first method we create will get the instance of a created database. This allows us to use the same database for the life of the app. If the database is already initialised we will use it, if not we will run the createDB method to create a new instance and then pass that.

Next, we create the method that will actually create the database

-(BOOL)createDB{
    NSString *docsDir;
    NSArray *dirPaths;
    // Get the documents directory
    dirPaths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    docsDir = dirPaths[0];
    // Build the path to the database file
    databasePath = [[NSString alloc] initWithString:
                    [docsDir stringByAppendingPathComponent: @"town.db"]];
    BOOL isSuccess = YES;
    NSFileManager *filemgr = [NSFileManager defaultManager];
    if ([filemgr fileExistsAtPath: databasePath ] == NO)
    {
        const char *dbpath = [databasePath UTF8String];
        if (sqlite3_open(dbpath, &database) == SQLITE_OK)
        {
            char *errMsg;
            const char *sql_stmt = "CREATE TABLE if not exists LocationInfo (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, Name TEXT, Description TEXT, Longitude TEXT, Lat TEXT)";
            
            if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
            {
                isSuccess = NO;
                NSLog(@"Failed to create table");
            }
            sqlite3_close(database);
            return  isSuccess;
        }
        else {
            isSuccess = NO;
            NSLog(@"Failed to open/create database");
        }
    }
    return isSuccess;
}

This method is pretty straight forward. First we create a database path in the apps document directory, then using the FileManager we can check if the database file exists. If it already does, there is nothing more we need to do. However if the file has not yet been created, we will go ahead and create the new file.

Using the method sqlite3_open to open a database at the file location is the quickest way to do this. The result is a database object that uses a newly created .db file at the specified location. Using this database object we can then execute our SQL statement to populate the database with our location table.

-(BOOL) clearLocations{
    const char *dbpath = [databasePath UTF8String];
    if (sqlite3_open(dbpath, &database) == SQLITE_OK)
    {
        NSString *insertSQL = @"DELETE FROM LocationInfo";
        const char *insert_stmt = [insertSQL UTF8String];
        sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
        if (sqlite3_step(statement) == SQLITE_DONE)
        {
            return YES;
        }
        else {
            return NO;
        }
    }
    return NO;
}

The next method we implement is the simple clearLocations. First we open the database using the set path. If the database is opened successfully (or already open), we can then execute the delete SQL. Using the sqlite3_prepare_v2 method, we step over the statement to check that the SQLite has been executed.

-(BOOL) insertLocation:(BuildingLocation*)location{
    const char *dbpath = [databasePath UTF8String];
    if (sqlite3_open(dbpath, &database) == SQLITE_OK)
    {
     
        NSNumber *longNumber = [NSNumber numberWithFloat:location.Long];
        NSNumber *latNumber = [NSNumber numberWithFloat:location.Lat];

        NSString *insertSQL = [NSString stringWithFormat:@"INSERT INTO LocationInfo (Name, Description, Longitude, Lat) values (\"%@\",\"%@\", \"%@\", \"%@\")",location.name, location.description, [longNumber stringValue], [latNumber stringValue]];
        const char *insert_stmt = [insertSQL UTF8String];
        sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
        if (sqlite3_step(statement) == SQLITE_DONE)
        {
            return YES;
        }
        else {
            NSLog(@"Error while updating. '%s'", sqlite3_errmsg(database));
            return NO;
        }
    }
    return NO;
}

Our next method looks at inserting a location into the database. We pass a BuildingLocation object to the method, then open the database. In the BuildingLocation, we are storing the long and lat as a float. To convert these to strings for the database, we must first create NSNumber objects from the float values.

NSNumber *longNumber = [NSNumber numberWithFloat:location.Long];
NSNumber *latNumber = [NSNumber numberWithFloat:location.Lat];

Then we create the SQL string that will be what we execute in the database.

NSString *insertSQL = [NSString stringWithFormat:@"INSERT INTO LocationInfo (Name, Description, Longitude, Lat) values (\"%@\",\"%@\", \"%@\", \"%@\")",location.name, location.description, [longNumber stringValue], [latNumber stringValue]];

Finally, we run this SQL string much the same we as the clearLocation method, checking that it was completed and returning true if that is the case.

-(NSArray*) getAllLocations{
    const char *dbpath = [databasePath UTF8String];
    NSString *querySQL;
    if (sqlite3_open(dbpath, &database) == SQLITE_OK)
    {
        NSString *querySQL = @"";
        
        querySQL = @"SELECT * FROM LocationInfo";
       
        const char *query_stmt = [querySQL UTF8String];
        NSMutableArray *resultArray = [[NSMutableArray alloc]init];
        if (sqlite3_prepare_v2(database, query_stmt, -1, &statement, NULL) == SQLITE_OK)
        {
            while (sqlite3_step(statement) == SQLITE_ROW)
            {
                BuildingLocation *location = [[BuildingLocation alloc] init];
                
                location.name = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
                location.description = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
                NSNumberFormatter * nFormatter = [[NSNumberFormatter alloc] init];
                [nFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
                NSNumber *numlong = [nFormatter numberFromString:[[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 4)]];
                NSNumber *numlat = [nFormatter numberFromString:[[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 5)]];

                location.Long = [numlong floatValue];
                location.Lat = [numlat floatValue];

                [resultArray addObject:location];
            }
            return resultArray;
        }
    }
    return nil;

}

The last method will get all the locations from the database and return an array of locations. As before, we first open the database, and then create the SQL and run it. The difference is, because we are now using a SELECT statement, sqlite3_prepare_v2 will return the rows of the database. sqlite3_step allows us to iterate over these rows and access the data. We then create a new BuildingLocation object and set its variables to the matching values in that database row.

Once they have all be set, we add the created object to the array. Once we have done iterating over the rows, we will then return the array.

Example of Use

DBHelper *db;
db = [DBHelper getSharedInstance];

BuildingLocation *locationObject = [[BuildingLocation alloc] init];
locationObject.name = "test";
locationObject.description = "this is a test place";
locationObject.Long = 1.458375;
locationObject.Lat =  52.105336;
           
[db insertLocation:locationObject];

NSMutableArray *locations;
locations = [db getAllLocations];

Here is an example of how we would use the code, first creating a new instance of the database, then we create a BuildingLocation object and insert it into the database. Finally, we get back the array of saved locations from the database.

If you have any comments or questions please ask below.

Apps I use on Nexus 9

Zachary Powell - 18/12/2015

My Nexus 9 is the next most used device within my work flow. I use it a lot through-out the day, both during my free time to play games, read books and magazines. But today, I want to take a moment to look at a few of the apps I use more during a day of work.

Dropbox

dropbox

Dropbox is my file sharing/backup cloud of choice. As I’m sure you know, there are a huge range of these systems available (Google drive, Onedrive, iCloud etc), but I have found that Dropbox is the only solution that works seamlessly across Windows/OSX/Ubuntu/Android/iOS and allows me to keep backups of my files from all Operating systems.

I use dropbox on my tablet to keep photos backed up, but also easily access any of my files I might need when I’m out and about. If a client needs to see a design for example, I will always have access to it.

The tablet app is also a great example of clean simple design. No fluff, just right to your data.

500px-Get_it_on_Google_play.svg

VLC

vlc

VLC is another great example of a program I use across the board. When I need to play a video file, I know that it can be played on VLC no problem. From time to time, a client might send me a video for something and with VLC I just know it will work. Again, I love using this app because of its material design and simple easy to use layout.

500px-Get_it_on_Google_play.svg

Teamviewer

teamviewer

Another big use for my tablet is accessing other computers remotely. These might be my own devices or clients. Often they need me to show them how to use software I have developed or have questions about certain features. Teamviewer allows me to do this pretty seamlessly, connecting to any device on any operating system quickly. I often leave my Mac at home and might just need to run a program while I’m out, or check on the progress of a new software build.

500px-Get_it_on_Google_play.svg

JuiceSSH

juicessh

As well as connection to full computers via Teamviewer, I also have a couple of headless servers, including a couple of Raspberry pi’s doing different tasks. One is a media server and another managers my local backup system. I also have a couple of remote servers for web development and other tasks. Using JuiceSSH, I can remotely connect to all of these via my tablet to easily check on them or run tasks.

500px-Get_it_on_Google_play.svg

Turbo Client

turbo-client

Again, just like the above two programs, I use Turbo Client to connect to servers via FTP. I have FTP servers running on my backup server and my remote servers, so if I need to access files remotely from these servers, this is the best app to use. Again it has a lovely clean design and makes accessing files very quick and easy.

500px-Get_it_on_Google_play.svg

Gmail

gmail

When I’m not out and working at my desk instead, the Nexus 9 works as a great extra screen. I tend to keep to just a couple of Apps while I’m at my desk, and most of the above apps are easier from a desktop, with far more screen space. However, what I do tend to use is either Gmail or Skype. Using Gmail on the tablet works great as a dedicated gmail terminal, as emails come in I can quickly glance down at the tablet (positioned below my main computer monitor) and read them, without stopping my current work flow. If they need a response too, I can bring up the desktop client to do such at a time that is suitable.

500px-Get_it_on_Google_play.svg

Skype

skype

The other app I will use while at my desk is Skype. Often this is used to speak with clients via video/voice chat or even via text. If I am having a busy day on Skype, I will move this to the tablet again so it does not disrupt my main work flow.

500px-Get_it_on_Google_play.svg

This is the first non development post. I will do these from time to time, explore my different work flows (what programs I use on the desktop etc), as well as reviewing hardware and software from time to time.

If you have anything you would like me to review or talk about, please comment below!

Adding custom Markers to Google Map

Zachary Powell - 17/12/2015

At this point, we have quite a nice little Google map application and we can now get the users location and display it on a Google map (see HERE if you have not caught up yet).

Now this works well, but what if we want to display extra data on the map? Perhaps there are locations that we want to highlight to the user. Below we will explore how we can add custom markers to the map and what can be done with them.

Plot marker with default pin

The most basic marker can be added with just a few lines of code.

LatLng position = new LatLng(53.779558, -4.042969);
MarkerOptions markerOption = new MarkerOptions().position(position);

markerOption.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
markerOption.title("Marker with default Pin");
mMap.addMarker(markerOption);

First we create the LatLng for the position we want to put the pin in (this is just a random point near England). We can then use the MarkerOptions object to set a few custom options. For the most simple, we can leave this with just the initial code, but we can also change many settings for the Marker – See HERE.

Changing the Icon, we can use the default section of marker colours.

We can also set the title. This is displayed when the user clicks the marker like below.

Screenshot_20151214-114449  Screenshot_20151214-114454

Plot marker with custom image

A great way to customise these pins, is to use our own image for the pin. For example:

private void plotMarkerWithCustomImage(){
    LatLng position = new LatLng(53.784426, -1.735840);
    MarkerOptions markerOption = new MarkerOptions().position(position);

    markerOption.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher));

    markerOption.title("Marker with custom image");
    mMap.addMarker(markerOption);
}

Gives us the below:

Screenshot_20151214-120522Screenshot_20151214-120526

Plot marker with canvas

Another more complex method, is to use a canvas to display other information in the marker itself. For example:

private void plotMarkerWithCanvas(){
   LatLng position = new LatLng(53.209322, -3.405762);
   MarkerOptions markerOption = new MarkerOptions().position(position);

   Bitmap.Config conf = Bitmap.Config.ARGB_8888;
   Bitmap bmp = Bitmap.createBitmap(80, 80, conf);
   Canvas canvas1 = new Canvas(bmp);

   Paint color = new Paint();
   color.setTextSize(35);
   color.setColor(Color.BLACK);

   canvas1.drawBitmap(BitmapFactory.decodeResource(getResources(),
                R.drawable.ic_launcher), 0,0, color);

   canvas1.drawText("User Name!", 30, 40, color);

   markerOption.icon(BitmapDescriptorFactory.fromBitmap(bmp));

   markerOption.title("Marker with canvas");
   mMap.addMarker(markerOption);
}

Here we create a new Bitmap image and a Canvas on that image. We can then create any custom Bitmap that we want. For example, adding text to the image, or loading another bitmap onto it.

Once created we can use this to set the icon. The above would give us:

Screenshot_20151214-121324 Screenshot_20151214-121328

For the complete example, as always check out the github project HERE. If you have any comments or questions please ask below

 

Display Users current location on Google Map

Zachary Powell - 14/12/2015

Before starting this guide, check out the Google Map example HERE if you have not already read it.

Now that we have a working Map, there is a range of examples we can cover. One that is very widely required, is displaying the users current location. When using a map, it is often the case that the user will want to be able to see where they currently are. Below we will explore what needs to be added to our Google Map example to display the users current location and update the marker as the user moves.

Manifest Changes

First we need to set some permissions in the Android manifest of our project. In Android studio, open the Manifest folder and open AndroidManifest.xml

We then need to add the below lines to manifest our side of the application tag.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

If you are using my Google Map Example code your manifest should then look like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zpwebsites.basicgooglemapexample">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />

        <activity
            android:name=".MapsActivity"
            android:label="@string/title_activity_maps">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

These permissions are required to get the users current location. We want the file location when available, but if there is no GPS connection, the coarse location can be used to get a rough user location based on a mobile signal.

Centre Map on Current Location

We are going to create a method called centerMapOnMyLocation(). This will be called once the map is loaded and will grab the users current location using GPS if available. The complete method:

private void centerMapOnMyLocation() {
        LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        Criteria criteria = new Criteria();

        Location location = null;

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

            location = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
        }

        if (location != null)
        {
            Long = location.getLongitude();
            Lat = location.getLatitude();
            mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                    new LatLng(location.getLatitude(), location.getLongitude()), 13));

            CameraPosition cameraPosition = new CameraPosition.Builder()
                    .target(new LatLng(location.getLatitude(), location.getLongitude()))      // Sets the center of the map to location user
                    .zoom(17)                   // Sets the zoom
                    .bearing(0)                // Sets the orientation of the camera to east
                    .tilt(40)                   // Sets the tilt of the camera to 30 degrees
                    .build();                   // Creates a CameraPosition from the builder
            mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
        }else{
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(53.779558, -4.042969), 5.5f));
            CharSequence text = "Sorry, we where unable to get your location, please check your GPS";
            int duration = Toast.LENGTH_LONG;
            Toast.makeText(this.getBaseContext(), text, duration).show();
        }
    }

First we set up a couple of variables which will be used within the method.

LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
Location location = null;

The LocationManager will be used to get the current location from the system. This handles querying the GPS for the location. The Location object will then be used to store the Location that is returned by the LocationManager.

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            location = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
        }

Next, as of Android API level 23 (Android version 6.0), we now have to check that the permissions we require have been accepted by the user. If they have been accepted, we can go ahead and use the LocationManager to call the .getLastKnownLocation(). This will return the last location from the system. If GPS is available, this will return the current location.

if (location != null)
        {
            Long = location.getLongitude();
            Lat = location.getLatitude();
           
            CameraPosition cameraPosition = new CameraPosition.Builder()
                    .target(new LatLng(Lat, Long))      // Sets the center of the map to location user
                    .zoom(17)                   // Sets the zoom
                    .bearing(0)                // Sets the orientation of the camera to east
                    .tilt(40)                   // Sets the tilt of the camera to 30 degrees
                    .build();                   // Creates a CameraPosition from the builder
            mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
        }

If the location has been returned, we can then get the locations longitude and latitude using the getter methods.  Once we have the location in the form of co-ordinates, we build a CameraPosition object, setting the target to the users position and setting up other camera settings, zoom level, bearing and tilt.

Finally, by passing this CameraPosition object to the animateCamera() method of the Google map object, we can move the maps view to centre on the users location.

else{
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(53.779558, -4.042969), 5.5f));
            CharSequence text = "Sorry, we where unable to get your location, please check your GPS";
            int duration = Toast.LENGTH_LONG;
            Toast.makeText(this.getBaseContext(), text, duration).show();
        }

We then use an else statement to cover if the location was not returned, either because the user declined the permissions or for some reason, theLocationManager failed to return the location object. In this else statement, we just move the camera to a pre-defined location and display a Toast message to let the user know we failed to get their position.

Then we need to edit our onMapRead() function to call our centerMapOnMyLocation() method, once our map is ready to use. We also call setMyLocationEnabled(true) so that the map will display a marker at the users location.

public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        mMap.setMyLocationEnabled(true);
        centerMapOnMyLocation();
}

Using LocationListener to update the users location

At this point, we have an app that centres to the users location on starting up the map, but if the user then moves it will not update the map. This is where we need to set up a LocationListener to monitor for location changes and update the map accordingly.

LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        LocationListener locationListener = new LocationListener() {
            public void onLocationChanged(Location location) {
                Long = location.getLongitude();
                Lat = location.getLatitude();
            }

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

            public void onProviderEnabled(String provider) {
            }

            public void onProviderDisabled(String provider) {
            }
        };

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
        }

Within the onCreate() method we add the above code. This sets up a LocationManager and a LocationListener. The LocationManager is the same as before using the Location service, while the LocationListener sets up the code to be run when a location changes. Within the onLocationChanged() method, we get the new Longitude and Latitude and update our Long and Lat variables.

Then, we need to run the same check to make sure the user has accepted our permissions again. If they have, we can then go ahead and register the LocationListener with the LocationManager. This allows the onLocationChanged() code to be run when the users location changes.

Complete Example

Once we have added all the above code, the app can not be run and display the current users location.

If you would like to check out the complete applications code, you can find it HERE. This code will be updated in the future to hold all example code for Google Maps.

As always if you have any questions please comment below.

“ Any fool can write code that a computer can understand. Good programmers write code that humans can understand. ”

-Martin Fowler