In this tutorial, we’ll play around with the Android Google Maps API. Showing the user the current location, lat/lng bounds, start navigation, enabling night mode etc.
Android Google Maps Current Location
Before we start implementing some cool android google maps features in our application, add the Google Maps v2 API key value in the meta-data
tag in the AndroidManifest.xml
file as mentioned in this tutorial.
Create a new project in Android Studio and select the template as Google Maps Activity.
Note: Google Play Services dependency will be added by default for this template.
Implement Google Play Location Services in your MapsActivity.java
class as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { private GoogleMap mMap; Location mLocation; GoogleApiClient mGoogleApiClient; private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; private LocationRequest mLocationRequest; private long UPDATE_INTERVAL = 15000; /* 15 secs */ private long FASTEST_INTERVAL = 5000; /* 5 secs */ private ArrayList permissionsToRequest; private ArrayList permissionsRejected = new ArrayList(); private ArrayList permissions = new ArrayList(); private final static int ALL_PERMISSIONS_RESULT = 101; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_maps); // Obtain the SupportMapFragment and get notified when the map is ready to be used. SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); permissions.add(ACCESS_FINE_LOCATION); permissions.add(ACCESS_COARSE_LOCATION); permissionsToRequest = findUnAskedPermissions(permissions); //get the permissions we have asked for before but are not granted.. //we will store this in a global list to access later. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (permissionsToRequest.size() > 0) requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT); } mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); connectClient(); } /** * Manipulates the map once available. * This callback is triggered when the map is ready to be used. * This is where we can add markers or lines, add listeners or move the camera. In this case, * we just add a marker near Sydney, Australia. * If Google Play services is not installed on the device, the user will be prompted to install * it inside the SupportMapFragment. This method will only be triggered once the user has * installed Google Play services and returned to the app. */ @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return; } mMap.setMyLocationEnabled(true); } public void connectClient() { mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); } private ArrayList findUnAskedPermissions(ArrayList wanted) { ArrayList result = new ArrayList(); for (String perm : wanted) { if (!hasPermission(perm)) { result.add(perm); } } return result; } @Override protected void onStart() { super.onStart(); if (mGoogleApiClient != null) { mGoogleApiClient.connect(); } } @Override protected void onResume() { super.onResume(); if (!checkPlayServices()) { Toast.makeText(getApplicationContext(),"Please install google play services",Toast.LENGTH_LONG).show(); } } @Override public void onConnected(@Nullable Bundle bundle) { if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return; } mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); startLocationUpdates(); } @Override public void onConnectionSuspended(int i) { } @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { } @Override public void onLocationChanged(Location location) { } private boolean checkPlayServices() { GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); int resultCode = apiAvailability.isGooglePlayServicesAvailable(this); if (resultCode != ConnectionResult.SUCCESS) { if (apiAvailability.isUserResolvableError(resultCode)) { apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST) .show(); } else finish(); return false; } return true; } protected void startLocationUpdates() { mLocationRequest = new LocationRequest(); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); mLocationRequest.setInterval(UPDATE_INTERVAL); mLocationRequest.setFastestInterval(FASTEST_INTERVAL); if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { Toast.makeText(getApplicationContext(), "Enable Permissions", Toast.LENGTH_LONG).show(); } LocationServices.FusedLocationApi.requestLocationUpdates( mGoogleApiClient, mLocationRequest, this); } private boolean hasPermission(String permission) { if (canMakeSmores()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED); } } return true; } private boolean canMakeSmores() { return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1); } @TargetApi(Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case ALL_PERMISSIONS_RESULT: for (String perms : permissionsToRequest) { if (!hasPermission(perms)) { permissionsRejected.add(perms); } } if (permissionsRejected.size() > 0) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) { showMessageOKCancel("These permissions are mandatory for the application. Please allow access.", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT); } } }); return; } } } break; } } private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(MapsActivity.this) .setMessage(message) .setPositiveButton("OK", okListener) .setNegativeButton("Cancel", null) .create() .show(); } @Override protected void onDestroy() { super.onDestroy(); stopLocationUpdates(); } public void stopLocationUpdates() { if (mGoogleApiClient.isConnected()) { LocationServices.FusedLocationApi .removeLocationUpdates(mGoogleApiClient, this); mGoogleApiClient.disconnect(); } } } |
In the above code mMap.setMyLocationEnabled(true);
is used to show the user’s current location.
The below image is the output of the application when the above code is run.
The blue dot is our current location. We need to focus the camera on the current location in the map to prevent zooming and scrolling manually.
Change the onConnected()
method as;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Override public void onConnected(@Nullable Bundle bundle) { if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return; } mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); LatLng latLng = new LatLng(mLocation.getLatitude(), mLocation.getLongitude()); CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 12); mMap.animateCamera(cameraUpdate); startLocationUpdates(); } |
In the above code 12 is the zoom level set. We can set the minimum and maximum zoom level using mMap.setMinZoomPreference(float v);
and mMap.setMaxZoomPreference(float v);
.
Android Google Maps Night Mode Project Structure
To enable night mode in the apps. We need to set the map style in the onMapReady method as;
mMap.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.mapstyle_night));
The mapstyle_night.json code is shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
[ { "featureType": "all", "elementType": "geometry", "stylers": [ { "color": "#242f3e" } ] }, { "featureType": "all", "elementType": "labels.text.stroke", "stylers": [ { "lightness": -80 } ] }, { "featureType": "administrative", "elementType": "labels.text.fill", "stylers": [ { "color": "#746855" } ] }, { "featureType": "administrative.locality", "elementType": "labels.text.fill", "stylers": [ { "color": "#d59563" } ] }, { "featureType": "poi", "elementType": "labels.text.fill", "stylers": [ { "color": "#d59563" } ] }, { "featureType": "poi.park", "elementType": "geometry", "stylers": [ { "color": "#263c3f" } ] }, { "featureType": "poi.park", "elementType": "labels.text.fill", "stylers": [ { "color": "#6b9a76" } ] }, { "featureType": "road", "elementType": "geometry.fill", "stylers": [ { "color": "#2b3544" } ] }, { "featureType": "road", "elementType": "labels.text.fill", "stylers": [ { "color": "#9ca5b3" } ] }, { "featureType": "road.arterial", "elementType": "geometry.fill", "stylers": [ { "color": "#38414e" } ] }, { "featureType": "road.arterial", "elementType": "geometry.stroke", "stylers": [ { "color": "#212a37" } ] }, { "featureType": "road.highway", "elementType": "geometry.fill", "stylers": [ { "color": "#746855" } ] }, { "featureType": "road.highway", "elementType": "geometry.stroke", "stylers": [ { "color": "#1f2835" } ] }, { "featureType": "road.highway", "elementType": "labels.text.fill", "stylers": [ { "color": "#f3d19c" } ] }, { "featureType": "road.local", "elementType": "geometry.fill", "stylers": [ { "color": "#38414e" } ] }, { "featureType": "road.local", "elementType": "geometry.stroke", "stylers": [ { "color": "#212a37" } ] }, { "featureType": "transit", "elementType": "geometry", "stylers": [ { "color": "#2f3948" } ] }, { "featureType": "transit.station", "elementType": "labels.text.fill", "stylers": [ { "color": "#d59563" } ] }, { "featureType": "water", "elementType": "geometry", "stylers": [ { "color": "#17263c" } ] }, { "featureType": "water", "elementType": "labels.text.fill", "stylers": [ { "color": "#515c6d" } ] }, { "featureType": "water", "elementType": "labels.text.stroke", "stylers": [ { "lightness": -20 } ] } ] |
Enable traffics in the map by the following code:
mMap.setTrafficEnabled(true);
mMap.setLatLngBoundsForCameraTarget();
is used to constrain the lat/lng center bounds of the focal point of the map (the camera target) so that users can only scroll and pan within these bounds.
To implement the above. Let’s take LatLngBounds for a part of city Adelaide for example.
Following is a snippet that’s put inside onMapReady method
1 2 3 4 5 6 7 8 9 10 11 |
final LatLngBounds ADELAIDE = new LatLngBounds( new LatLng(-35.0, 138.58), new LatLng(-34.9, 138.61)); final CameraPosition ADELAIDE_CAMERA = new CameraPosition.Builder() .target(new LatLng(-34.92873, 138.59995)).zoom(20.0f).bearing(0).tilt(0).build(); mMap.setLatLngBoundsForCameraTarget(ADELAIDE); mMap.addMarker(new MarkerOptions() .position(new LatLng(-34.92873, 138.59995)) .title("My Marker")); mMap.animateCamera(CameraUpdateFactory.newCameraPosition(ADELAIDE_CAMERA)); |
Following is the output of the application.
To start navigation app we need to pass the latitude and longitude of the destination in the following way:
1 2 3 4 5 6 7 |
String nav_lat=22.7213129; String nav_lng=75.8763886; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=" + nav_lat + "," + nav_lng)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); |
The above code can be invoked from an onClickListener
linked to a Button/any other widget.
This brings an end to android google maps tutorial. You can download the Android GoogleMapsFeatures Project from the below link.