This is my first time working with google maps in android.I was able to create a map showing my current user location.
However, I would like to display multiple markers whose data I'm fetching using volley. To do this I'm using a backgroundTask class with a singleton class. The backgroundTask class is the one returning the arrayList of the location data.
For this reason I need to populate my BackgroungTask class with the arrayList before my MapActivity become active and access the arraylist of objects in onCreate but it seems the MapActivity loads very fast and a reference to the arrayList of objects from the BackgroungTask class is always null.
This is the BackgroundTask class which is fetching the data
package com.example.carwashplaces;
import android.content.Context;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public class BackgroundTask {
private Context context;
private ArrayList<ModelLocation> locationArrayList = new ArrayList<>();
String json_url = "http://histogenetic-exhaus.000webhostapp.com/location.php";
public BackgroundTask(Context context) {
this.context = context;
}
public ArrayList<ModelLocation> getLocationArrayList(){
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest (
Request.Method.POST,
json_url,
null,
new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
int count = 0;
while (count<response.length()){
try {
JSONObject jsonObject = response.getJSONObject(count);
ModelLocation modelLocation = new ModelLocation(
jsonObject.getString("id"),
jsonObject.getString("name"),
jsonObject.getString("latitude"),
jsonObject.getString("longitude"),
jsonObject.getString("staff"),
jsonObject.getString("comment"),
jsonObject.getString("phone"));
locationArrayList.add(modelLocation);
count++;
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, "Error fetching car wash places... ", Toast.LENGTH_SHORT).show();
error.printStackTrace();
}
});
MySingleton.getInstance(context).addToRequestque(jsonArrayRequest);
return locationArrayList;
}
}
This is the MapActivity where I want to display the locations.
package com.example.carwashplaces;
import android.content.Intent;
import android.content.IntentSender;
import android.location.Location;
import android.os.Bundle;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.SettingsClient;
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.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.AutocompletePrediction;
import com.google.android.libraries.places.api.model.AutocompleteSessionToken;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.TypeFilter;
import com.google.android.libraries.places.api.net.FetchPlaceRequest;
import com.google.android.libraries.places.api.net.FetchPlaceResponse;
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsRequest;
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsResponse;
import com.google.android.libraries.places.api.net.PlacesClient;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.mancj.materialsearchbar.MaterialSearchBar;
import com.mancj.materialsearchbar.adapter.SuggestionsAdapter;
import com.skyfishjy.library.RippleBackground;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback {
private GoogleMap mMap;
private FusedLocationProviderClient mFusedLocationProviderClient;
private PlacesClient placesClient;
private List<AutocompletePrediction> predictionList;
private ArrayList<ModelLocation> locations;
private BackgroundTask backgroundTask;
private Location mLastKnownLocation;
private LocationCallback locationCallback;
private MaterialSearchBar materialSearchBar;
private View mapView;
private Button btnFind;
private RippleBackground rippleBg;
private final float DEFAULT_ZOOM = 18;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
//init views
materialSearchBar = findViewById(R.id.searchBar);
btnFind = findViewById(R.id.btnFind);
rippleBg = findViewById(R.id.ripple_bg);
//an object of backgroung task
backgroundTask = new BackgroundTask(MapActivity.this);
locations = new ArrayList<>();
//getting location data from background task
locations = backgroundTask.getLocationArrayList();
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
mapView = mapFragment.getView();
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(MapActivity.this);
Places.initialize(MapActivity.this, "MY_KEY");
placesClient = Places.createClient(this);
final AutocompleteSessionToken token = AutocompleteSessionToken.newInstance();
materialSearchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() {
@Override
public void onSearchStateChanged(boolean enabled) {
}
@Override
public void onSearchConfirmed(CharSequence text) {
startSearch(text.toString(), true, null, true);
}
@Override
public void onButtonClicked(int buttonCode) {
if (buttonCode == MaterialSearchBar.BUTTON_NAVIGATION){
//opening or closing a navigation drawer
}else if (buttonCode == MaterialSearchBar.BUTTON_BACK){
materialSearchBar.disableSearch();
}
}
});
materialSearchBar.addTextChangeListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
FindAutocompletePredictionsRequest predictionsRequest = FindAutocompletePredictionsRequest.builder()
.setCountry("ke")
.setTypeFilter(TypeFilter.ADDRESS)
.setSessionToken(token)
.setQuery(s.toString())
.build();
placesClient.findAutocompletePredictions(predictionsRequest).addOnCompleteListener(new OnCompleteListener<FindAutocompletePredictionsResponse>() {
@Override
public void onComplete(@NonNull Task<FindAutocompletePredictionsResponse> task) {
if (task.isSuccessful()){
//predictions found, find out what suggestions we have from google
FindAutocompletePredictionsResponse predictionsResponse = task.getResult();
if (predictionsResponse != null){
predictionList = predictionsResponse.getAutocompletePredictions();
//converting predictionList into a list of string
List<String> suggestionsList = new ArrayList<>();
for (int i = 0; i < predictionList.size(); i++){
AutocompletePrediction prediction = predictionList.get(i);
suggestionsList.add(prediction.getFullText(null).toString());
}
//pass suggestion list to our MaterialSearchBar
materialSearchBar.updateLastSuggestions(suggestionsList);
if (!materialSearchBar.isSuggestionsVisible()){
materialSearchBar.showSuggestionsList();
}
}
}
else {
//some error
Log.i("mytag", "prediction fetching task failed");
}
}
});
}
@Override
public void afterTextChanged(Editable s) {
}
});
materialSearchBar.setSuggstionsClickListener(new SuggestionsAdapter.OnItemViewClickListener() {
@Override
public void OnItemClickListener(int position, View v) {
// seek the longitude and latitude of that suggestion
if (position >= predictionList.size()){
return;
}
AutocompletePrediction selectedPrediction = predictionList.get(position);
String suggestion = materialSearchBar.getLastSuggestions().get(position).toString();
materialSearchBar.setText(suggestion);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
materialSearchBar.clearSuggestions();
}
},1000);
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (imm != null){
imm.hideSoftInputFromWindow(materialSearchBar.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
}
String placeId = selectedPrediction.getPlaceId();
List<Place.Field> placeFields = Arrays.asList(Place.Field.LAT_LNG);// get latitude and longitude of a place
FetchPlaceRequest fetchPlaceRequest = FetchPlaceRequest.builder(placeId, placeFields).build();
placesClient.fetchPlace(fetchPlaceRequest).addOnSuccessListener(new OnSuccessListener<FetchPlaceResponse>() {
@Override
public void onSuccess(FetchPlaceResponse fetchPlaceResponse) {
// place found, update camera Factory
Place place = fetchPlaceResponse.getPlace();
Log.i("mytag", "place found: "+place.getName());
LatLng latLngOfPlace = place.getLatLng();
if (latLngOfPlace != null){
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLngOfPlace, DEFAULT_ZOOM));
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ApiException){
ApiException apiException = (ApiException) e;
apiException.printStackTrace();
int statusCode = apiException.getStatusCode();
Log.i("mytag", "place not found: " +e.getMessage());
Log.i("mytag", "status code: "+statusCode);
}
}
});
}
@Override
public void OnItemDeleteListener(int position, View v) {
}
});
btnFind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//get the current location of the marker
LatLng currentMarkerLocation = mMap.getCameraPosition().target;
rippleBg.startRippleAnimation();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
rippleBg.stopRippleAnimation();
Toast.makeText(MapActivity.this, "Nearest Car Wash", Toast.LENGTH_SHORT).show();
}
},3000);
}
});
}
//Called when the map is loaded
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMyLocationEnabled(true);
mMap.getUiSettings().setMyLocationButtonEnabled(true);
if (mapView != null && mapView.findViewById(Integer.parseInt("1")) != null){
View locationButton = ((View) mapView.findViewById(Integer.parseInt("1")).getParent()).findViewById(Integer.parseInt("2"));
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) locationButton.getLayoutParams();
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, 0);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
layoutParams.setMargins(0, 0, 40, 180);
}
//check if gps is enabled or not and then request user to enable it
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
SettingsClient settingsClient = LocationServices.getSettingsClient(MapActivity.this);
Task<LocationSettingsResponse> task = settingsClient.checkLocationSettings(builder.build());
task.addOnSuccessListener(MapActivity.this, new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
getDeviceLocation();
setMarkersToCarWashes();
}
});
task.addOnFailureListener(MapActivity.this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ResolvableApiException){
ResolvableApiException resolvable = (ResolvableApiException) e;
try {
resolvable.startResolutionForResult(MapActivity.this, 51);
} catch (IntentSender.SendIntentException ex) {
ex.printStackTrace();
}
}
}
});
mMap.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
@Override
public boolean onMyLocationButtonClick() {
if (materialSearchBar.isSuggestionsVisible())
materialSearchBar.clearSuggestions();
if (materialSearchBar.isSearchEnabled())
materialSearchBar.disableSearch();
return false;
}
});
setMarkersToCarWashes();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 51) {
if (resultCode == RESULT_OK) {
getDeviceLocation();
setMarkersToCarWashes();
}
}
}
private void setMarkersToCarWashes() {
if (locations != null){
for(int i = 0; i < locations.size(); i++){
double lati = Double.parseDouble(locations.get(i).getLatitude());
double longLat = Double.parseDouble(locations.get(i).getLongitude());
//set markers to car wash places
mMap.addMarker(new MarkerOptions().position(
new LatLng(lati, longLat))
.title(locations.get(i).getName())
.snippet(locations.get(i).getComment()));
}
}
}
private void getDeviceLocation() {
mFusedLocationProviderClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
@Override
public void onComplete(@NonNull Task<Location> task) {
if (task.isSuccessful()){
mLastKnownLocation = task.getResult();
if (mLastKnownLocation != null){
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
}else {
final LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationCallback = new LocationCallback(){
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
if (locationResult == null){
return;
}
mLastKnownLocation = locationResult.getLastLocation();
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
mFusedLocationProviderClient.removeLocationUpdates(locationCallback);
}
};
mFusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, null);
}
}else {
Toast.makeText(MapActivity.this, "Unable to get last Location", Toast.LENGTH_SHORT).show();
}
}
});
}
}
And This is the part of the code in MapActivity where I'm trying to get the arraylist from the BackgroundTask class
//an object of backgroung task
backgroundTask = new BackgroundTask(MapActivity.this);
locations = new ArrayList<>();
//getting location data from background task
locations = backgroundTask.getLocationArrayList();
How do I display the markers in onMapReady method i.e populate the BackgroundTask before the onMapReady method executes?