-
-
Save serceberka/0935e185e663a3be13eb13f4b9e0d5ac to your computer and use it in GitHub Desktop.
Error resolved: java.lang.IllegalStateException: GoogleApiClient is not connected yet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package edu.monash.mymonashlibrary.service; | |
import android.Manifest; | |
import android.app.PendingIntent; | |
import android.content.Intent; | |
import android.content.pm.PackageManager; | |
import android.graphics.Color; | |
import android.location.Location; | |
import android.os.Bundle; | |
import android.graphics.Bitmap; | |
import android.graphics.drawable.Drawable; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import android.support.v4.content.ContextCompat; | |
import android.support.v4.content.res.ResourcesCompat; | |
import android.support.v7.app.AppCompatActivity; | |
import android.widget.Toast; | |
import com.google.android.gms.common.ConnectionResult; | |
import com.google.android.gms.common.api.GoogleApiClient; | |
import com.google.android.gms.common.api.Result; | |
import com.google.android.gms.common.api.ResultCallback; | |
import com.google.android.gms.location.Geofence; | |
import com.google.android.gms.location.GeofencingRequest; | |
import com.google.android.gms.location.LocationListener; | |
import com.google.android.gms.location.LocationRequest; | |
import com.google.android.gms.location.LocationServices; | |
import com.google.android.gms.maps.CameraUpdateFactory; | |
import com.google.android.gms.maps.GoogleMap; | |
import com.google.android.gms.maps.OnMapReadyCallback; | |
import com.google.android.gms.maps.SupportMapFragment; | |
import com.google.android.gms.maps.UiSettings; | |
import com.google.android.gms.maps.model.BitmapDescriptorFactory; | |
import com.google.android.gms.maps.model.CircleOptions; | |
import com.google.android.gms.maps.model.LatLng; | |
import com.google.android.gms.maps.model.LatLngBounds; | |
import com.google.android.gms.maps.model.Marker; | |
import com.google.android.gms.maps.model.MarkerOptions; | |
import org.jetbrains.annotations.Contract; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Random; | |
import edu.monash.mymonashlibrary.R; | |
import edu.monash.mymonashlibrary.collection.LibraryCollection; | |
import edu.monash.mymonashlibrary.entity.Library; | |
import static edu.monash.mymonashlibrary.utility.image.Drawable.drawableToBitmap; | |
/** | |
* https://developers.google.com/maps/documentation/android-api/controls | |
* https://stackoverflow.com/questions/34977333/googleapiclient-is-not-connected-despite-onconnect-being-called | |
* https://code.tutsplus.com/tutorials/how-to-work-with-geofences-on-android--cms-26639 | |
* https://github.com/googlemaps/android-samples/blob/master/ApiDemos/app/src/main/java/com/example/mapdemo/UiSettingsDemoActivity.java | |
* zoom into building when customer marker clicked | |
* update library latlng by making greater than 3 sig figs NOPE | |
* may want to implement permission utils | |
* https://github.com/googlemaps/android-samples/blob/master/ApiDemos/app/src/main/java/com/example/mapdemo/PermissionUtils.java | |
* may want to also customise how map looks e.g. darker map with oragne road lines | |
* may want to show how you want to travel and the route you should take REDUNDANT | |
* since already connects to google maps | |
*/ | |
public class MapsActivity extends AppCompatActivity | |
implements OnMapReadyCallback, GoogleMap.OnMarkerClickListener, ResultCallback, | |
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener{ | |
private GoogleMap mMap = null; | |
private UiSettings mUiSettings = null; | |
private PendingIntent mGeofencingPendingIntent = null; | |
private GoogleApiClient mGoogleApiClient = null; | |
private LocationRequest locationRequest = null; | |
private static final int CURRENT_LOCATION_PERMISSION_REQUEST_CODE = 1; | |
private static final int LOCATION_LAYER_PERMISSION_REQUEST_CODE = 2; | |
private boolean mLocationPermissionDenied = false; | |
/* Normal and fastest update intervals for location service */ | |
private final long UPDATE_INTERVAL = 10 * 1000; /* 10 seconds */ | |
private final long FASTEST_INTERVAL = 2 * 1000; /* 2 seconds */ | |
/* Radius of geofence | |
* Using default radius of 60 meters for all libraries. | |
* In future may add capability to specify radius according to library. | |
*/ | |
private static final Float GEOFENCE_RADIUS = 60.0f; | |
/* Used existing libraryList */ | |
private static LibraryCollection libraryCollection = new LibraryCollection(); | |
/* List containing Geofence's */ | |
private static List<Geofence> geofenceCollection = new ArrayList<>(); | |
/* Map containing key value pairs of Integer and List<Double> respectively | |
* k = libraryId, v = lat, long, rad | |
*/ | |
private static Map<Integer, List<Double>> geofenceMap = new HashMap<>(); | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_maps); | |
/* Create a new google api client */ | |
mGoogleApiClient = new GoogleApiClient.Builder(MapsActivity.this) | |
.addConnectionCallbacks(MapsActivity.this) | |
.addOnConnectionFailedListener(MapsActivity.this) | |
.addApi(LocationServices.API) | |
.build(); | |
/* 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); | |
} | |
@Override | |
protected void onStart() { | |
super.onStart(); | |
/* Connect to the API client */ | |
if (mGoogleApiClient != null) { | |
mGoogleApiClient.connect(); | |
} | |
} | |
@Override | |
protected void onStop() { | |
super.onStop(); | |
/* Invalidate client by disconnecting */ | |
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, MapsActivity.this); | |
/* Check if we are connected, if we are not connected | |
* and we try to stop the app will crash | |
*/ | |
if (mGoogleApiClient != null) { | |
mGoogleApiClient.disconnect(); | |
} | |
} | |
@Override | |
public void onConnected(Bundle savedInstanceState) { | |
List<Library> libraryList = libraryCollection.getLibraryList(); | |
for(Library library: libraryList) { | |
/* Add custom marker at Monash libraries */ | |
createCustomMarker(library.getCampus(), library.getName(), library.getCoordinates()); | |
/* Create geofences around libraries */ | |
geofenceCollection.add(createGeofence(library.getId().toString(), library.getCoordinates(), GEOFENCE_RADIUS)); | |
/* Create new ArrayList and add lat, long and rad values to it */ | |
List<Double> latLongRad = new ArrayList<>(); | |
latLongRad.add(library.getCoordinates().latitude); | |
latLongRad.add(library.getCoordinates().longitude); | |
latLongRad.add(Double.valueOf(GEOFENCE_RADIUS)); | |
/* Put entries in the geofenceMap */ | |
geofenceMap.put(library.getId(), latLongRad); | |
} | |
/* Add geofences to client */ | |
addGeofences(); | |
/* Get last known location */ | |
/* Try this | |
* https://developer.android.com/training/permissions/requesting.html | |
* if you have time | |
*/ | |
Location currentLocation = null; | |
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) | |
== PackageManager.PERMISSION_GRANTED) { | |
currentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); | |
} | |
if (currentLocation != null) { | |
String msg = "Started Location: " + | |
Double.toString(currentLocation.getLatitude()) + ", " + | |
Double.toString(currentLocation.getLongitude()); | |
Toast toast = Toast.makeText(MapsActivity.this, msg, Toast.LENGTH_SHORT); | |
toast.show(); | |
} | |
/* Start polling for new location updates */ | |
startLocationUpdates(); | |
} | |
/* Inform user if connection is suspended */ | |
@Override | |
public void onConnectionSuspended(int suspend) { | |
if (suspend == CAUSE_SERVICE_DISCONNECTED) { | |
Toast toast = Toast.makeText(getApplicationContext(), | |
"GPS Error: Disconnected\nPlease reconnect", Toast.LENGTH_SHORT); | |
toast.show(); | |
} else if (suspend == CAUSE_NETWORK_LOST) { | |
Toast toast = Toast.makeText(getApplicationContext(), | |
"Network Error: Disconnected\nPlease reconnect", Toast.LENGTH_SHORT); | |
toast.show(); | |
} | |
} | |
/* Trigger new location updates at interval */ | |
protected void startLocationUpdates() { | |
/* Create location request */ | |
locationRequest = LocationRequest.create() | |
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) | |
.setInterval(UPDATE_INTERVAL) | |
.setFastestInterval(FASTEST_INTERVAL); | |
/* Check for permissions to access Location */ | |
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) | |
== PackageManager.PERMISSION_GRANTED) { | |
/* Request location updates */ | |
LocationServices.FusedLocationApi.requestLocationUpdates( | |
mGoogleApiClient, locationRequest, MapsActivity.this); | |
} | |
} | |
/* Inform user if connection failed */ | |
@Override | |
public void onConnectionFailed(@NonNull ConnectionResult result) { | |
if (!result.isSuccess()) { | |
Toast toast = Toast.makeText(getApplicationContext(), | |
"Connection Error: No connections available", Toast.LENGTH_SHORT); | |
toast.show(); | |
} | |
} | |
@Override | |
public void onLocationChanged(Location location) { | |
/* New location determined */ | |
String msg = "Changed Location: " + | |
Double.toString(location.getLatitude()) + ", " + | |
Double.toString(location.getLongitude()); | |
Toast toast = Toast.makeText(MapsActivity.this, msg, Toast.LENGTH_SHORT); | |
toast.show(); | |
} | |
/** | |
* 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; | |
mUiSettings = mMap.getUiSettings(); | |
if (checkReady()) { | |
//Set marker click listener | |
mMap.setOnMarkerClickListener(MapsActivity.this); | |
/* Create bound to set maximum zoom out level to state of Victoria */ | |
final LatLngBounds VICTORIA = new LatLngBounds( | |
new LatLng(-39.185, 140.988), new LatLng(-33.965, 146.415)); | |
/* Constrain the camera target to VICTORIA bounds */ | |
mMap.setLatLngBoundsForCameraTarget(VICTORIA); | |
/* This map will behave according to the following rules */ | |
stateZoomButtons(true); | |
stateZoomGestures(false); | |
stateCompass(true); | |
stateCurrentLocation(true); | |
stateRotateGestures(true); | |
stateScrollGestures(true); | |
stateTiltGestures(true); | |
/* Add a marker in Melbourne and move the camera */ | |
LatLng melbourne = new LatLng(-37.814, 144.963); | |
mMap.addMarker(new MarkerOptions() | |
.position(melbourne) | |
.title("Melbourne") | |
.snippet("Melbourne City")); | |
mMap.moveCamera(CameraUpdateFactory.newLatLng(melbourne)); | |
} | |
} | |
@Override | |
public boolean onMarkerClick(final Marker marker) { | |
switch(marker.getSnippet()) { | |
/* View all library buildings */ | |
default: | |
intoBuilding(marker); | |
break; | |
} | |
/* Returning false to retain default behaviour */ | |
return false; | |
} | |
private void intoBuilding(final Marker marker) { | |
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(marker.getPosition(), 18)); | |
} | |
/* Modify the marker placed at library */ | |
private Marker createCustomMarker(String location, String library, LatLng coords) { | |
Drawable xmlResource = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_local_library_24dp, null); | |
Bitmap bitmap = drawableToBitmap(xmlResource); | |
return mMap.addMarker(new MarkerOptions() | |
.icon(BitmapDescriptorFactory.fromBitmap(bitmap)) | |
.anchor(0.0f, 1.0f) | |
.position(coords) | |
.title(location) | |
.snippet(library)); | |
} | |
/* Blueprint for creating a geofence */ | |
@NonNull | |
private Geofence createGeofence( | |
String id, LatLng latLng, Float radius) { | |
return new Geofence.Builder() | |
.setRequestId(id) | |
.setCircularRegion(latLng.latitude, latLng.longitude, radius) | |
.setExpirationDuration(Geofence.NEVER_EXPIRE) | |
.setLoiteringDelay(10 * 1000) //10seconds | |
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT | | |
Geofence.GEOFENCE_TRANSITION_DWELL) | |
.build(); | |
} | |
/* Specify how to monitor and trigger Geofence events */ | |
@Nullable | |
private GeofencingRequest getGeofencingRequest() { | |
if (geofenceCollection != null) { | |
GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); | |
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER); | |
builder.addGeofences(geofenceCollection); | |
return builder.build(); | |
} | |
return null; | |
} | |
private PendingIntent getGeofencePendingIntent() { | |
if (mGeofencingPendingIntent != null) { | |
return mGeofencingPendingIntent; | |
} | |
Intent intent = new Intent(MapsActivity.this, GeofenceTransitionService.class); | |
/* We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when | |
* calling addGeofences() and removeGeofences(). | |
*/ | |
return PendingIntent.getService( | |
MapsActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); | |
} | |
@SuppressWarnings("unchecked") | |
private void addGeofences() { | |
/* Check for permissions to access Location */ | |
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) | |
== PackageManager.PERMISSION_GRANTED) { | |
LocationServices.GeofencingApi.addGeofences( | |
mGoogleApiClient, | |
getGeofencingRequest(), | |
getGeofencePendingIntent() | |
).setResultCallback(MapsActivity.this); | |
} | |
} | |
@Override | |
public void onResult(@NonNull Result result) { | |
if (result.getStatus().isSuccess()) { | |
Toast toast = Toast.makeText(getApplicationContext(), | |
"Now monitoring geofences", Toast.LENGTH_SHORT); | |
toast.show(); | |
drawGeofences(); | |
} else { | |
Toast toast = Toast.makeText(getApplicationContext(), | |
"Geofence error: Unable to display fence boundaries", Toast.LENGTH_SHORT); | |
toast.show(); | |
} | |
} | |
private void drawGeofences() { | |
if (geofenceCollection != null) { | |
for (Geofence geofence: geofenceCollection) { | |
Random random = new Random(Long.valueOf(geofence.getRequestId())); | |
Integer intRequestId = Integer.valueOf(geofence.getRequestId()); | |
if (geofenceMap.containsKey(intRequestId)) { | |
CircleOptions circleOptions = new CircleOptions() | |
.center(new LatLng(geofenceMap.get(intRequestId).get(0), | |
geofenceMap.get(intRequestId).get(1))) | |
.radius(geofenceMap.get(intRequestId).get(2)) | |
.strokeColor(Color.argb(50, 100, 100, 100)) | |
.fillColor(Color | |
.argb(100, random.nextInt(256), random.nextInt(256), random.nextInt(256))); | |
mMap.addCircle(circleOptions); | |
} | |
} | |
} | |
} | |
/* Check if mMap is loaded */ | |
@Contract(pure = true) | |
private boolean checkReady() { | |
if (mMap == null) { | |
return false; | |
} | |
return true; | |
} | |
/* Enable zoom buttons */ | |
public void stateZoomButtons(boolean state) { | |
mUiSettings.setZoomControlsEnabled(state); | |
} | |
/* Disable zoom gestures */ | |
public void stateZoomGestures(boolean state) { | |
mUiSettings.setZoomGesturesEnabled(state); | |
} | |
/* Enable compass */ | |
public void stateCompass(boolean state) { | |
mUiSettings.setCompassEnabled(state); | |
} | |
/* Request fine location permission | |
public void requestLocationPermission(int requestCode) { | |
if (ActivityCompat.shouldShowRequestPermissionRationale( | |
MapsActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)) { | |
//Display dialogue with rationale | |
} | |
}*/ | |
/* Try to enable current location information. | |
* If it can't be enabled, show the user an error message. | |
*/ | |
public void stateCurrentLocation(boolean state) { | |
/* Check for permissions to access Location */ | |
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) | |
== PackageManager.PERMISSION_GRANTED) { | |
mMap.setMyLocationEnabled(state); | |
} else { | |
/* Instead of informing user, create PermissionUtil class | |
* to handle no permission granted case | |
*/ | |
Toast toast = Toast.makeText(getApplicationContext(), | |
"Permission error: You do not have permission to " + | |
"use 'current' location feature. ", | |
Toast.LENGTH_SHORT); | |
toast.show(); | |
} | |
} | |
public void stateScrollGestures(boolean state) { | |
mUiSettings.setScrollGesturesEnabled(state); | |
} | |
public void stateTiltGestures(boolean state) { | |
mUiSettings.setTiltGesturesEnabled(state); | |
} | |
public void stateRotateGestures(boolean state) { | |
mUiSettings.setRotateGesturesEnabled(state); | |
} | |
/*@Override | |
protected void onResumeFragments() { | |
//Need PermissionUtil | |
super.onResumeFragments(); | |
if (mLocationPermissionDenied) { | |
} | |
}*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment