in my Android inside an activity I have an asynchronous call to a google maps API inside a method see the code below :
public void reverseGeocode(String lat, String lng) {
String geocodeApiUrl = getUrl(lat, lng);
GoogleService googleService = GoogleServiceBuilder.buildService(GoogleService.class);
Call<Address> geocodeRequest = googleService.geocodeAddress(geocodeApiUrl);
geocodeRequest.enqueue(new Callback<Address>() {
#Override
public void onResponse(Call<Address> call, Response<Address> response) {
if (response.errorBody() == null) {
if (response.body().getResults().length > 0) {
if (response.body().getResults()[0].getFormatted_address() != null) {
dropOffAddress = response.body().getResults()[0].getFormatted_address();
}
}
} else {
Toast.makeText(AdressPicker.this, "An error has occured", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<Address> call, Throwable t) {
Toast.makeText(AdressPicker.this, "An error has occured", Toast.LENGTH_LONG).show();
}
});
}
this method is called inside the Google map marker 's onMarkerDragEnd callback function , see code below :
#Override
public void onMarkerDragEnd(Marker marker) {
double lat = marker.getPosition().latitude;
double lng = marker.getPosition().longitude;
reverseGeocode(String.valueOf(lat), String.valueOf(lng));
marker.setSnippet(dropOffAddress);
marker.showInfoWindow();
editor.putString("dropOffAddress", dropOffAddress);
editor.putString("lat", String.valueOf(lat));
editor.putString("lng", String.valueOf(lng));
editor.apply();
}
The problem here is the first time when I move the marker the reverseGeocode method is called but it is not awaited and the onMarkerDragEnd continue its execution and puts null inside the value of the dropOffAddress variable which is intended to be displayed inside the snippet of the marker.
My question here is HOW to WAIT for the reverseGeocode method'is response inside onMarkerDragEnd before continuing executing ?
Thank you.
the only thing you. need is marker, just pass it as a parameter in your retrofit api call something like this.
public void reverseGeocode(String lat, String lng, Marker marker) {
String geocodeApiUrl = getUrl(lat, lng);
GoogleService googleService = GoogleServiceBuilder.buildService(GoogleService.class);
Call<Address> geocodeRequest = googleService.geocodeAddress(geocodeApiUrl);
geocodeRequest.enqueue(new Callback<Address>() {
#Override
public void onResponse(Call<Address> call, Response<Address> response) {
if (response.errorBody() == null) {
if (response.body().getResults().length > 0) {
if (response.body().getResults()[0].getFormatted_address() != null) {
dropOffAddress = response.body().getResults()[0].getFormatted_address();
applyGeoCode(marker)
}
}
} else {
Toast.makeText(AdressPicker.this, "An error has occured", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<Address> call, Throwable t) {
Toast.makeText(AdressPicker.this, "An error has occured", Toast.LENGTH_LONG).show();
}
});
}
now from here call a method passing the marker
public void applyGeoCode(Marker marker){
double lat = marker.getPosition().latitude;
double lng = marker.getPosition().longitude;
marker.setSnippet(dropOffAddress);
marker.showInfoWindow();
editor.putString("dropOffAddress", dropOffAddress);
editor.putString("lat", String.valueOf(lat));
editor.putString("lng", String.valueOf(lng));
editor.apply();
}
and call like this
#Override
public void onMarkerDragEnd(Marker marker) {
reverseGeocode(String.valueOf(lat), String.valueOf(lng), marker);
}
You can use CountDownLatch from the java.utils.Concurrent class.
Here is an example:
public class DoSomething {
private Address dropoffAddr;
private CountDownLatch dropoffAddrLatch = new CountDownLatch(1);
public void getAddressCallback(Address addr) {
dropoffAddr = addr;
dropoffAddrLatch.countDown();
}
#Override
public void onMarkerDragEnd(Marker marker) {
//your stuff
reverseGeocode(...); //you should call getAddressCallback once you have everything
dropOffAddrLatch.await(); //this will wait until getAddressCallBack has returned,
// you can call dropoffAddrLatch.countDown() wherever you want.
//finish your stuff
}
}
Related
This is my example where I am tapping on the map to get the tapped point coordinates and to send a search request to know more details about that place, for example, city, street. But the response is always null values.
public class MainActivity extends AppCompatActivity implements CameraListener, InputListener, Session.SearchListener {
private ActivityMainBinding binding;
private SearchManager searchManager;
private Session searchSession;
private SearchOptions searchOptions;
private MapObjectCollection mapObjectCollection;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MapKitFactory.setApiKey("your api key");
MapKitFactory.initialize(this);
searchOptions = new SearchOptions();
searchOptions.setSearchTypes(SearchType.GEO.value);
searchOptions.setGeometry(true);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.mapview.getMap().setNightModeEnabled((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES);
searchManager = SearchFactory.getInstance().createSearchManager(SearchManagerType.COMBINED);
binding.mapview.getMap().addCameraListener(this);
binding.mapview.getMap().addInputListener(this);
mapObjectCollection = binding.mapview.getMap().getMapObjects();
binding.mapview.getMap().move(
new CameraPosition(new Point(55.751574, 37.573856), 11.0f, 0.0f, 0.0f),
new Animation(Animation.Type.SMOOTH, 0),
null);
}
#Override
protected void onStop() {
super.onStop();
binding.mapview.onStop();
MapKitFactory.getInstance().onStop();
}
#Override
protected void onStart() {
super.onStart();
binding.mapview.onStart();
MapKitFactory.getInstance().onStart();
}
public void submitQueryByName(String query) {
searchSession = searchManager.submit(
query,
Geometry.fromPoint(new Point(40.177200, 44.503490)),
searchOptions,
this);
}
public void submitQueryByPoint(Point point) {
searchSession = searchManager.submit(
point,
11,
searchOptions,
this);
}
#Override
public void onCameraPositionChanged(#NonNull Map map, #NonNull CameraPosition cameraPosition, #NonNull CameraUpdateReason cameraUpdateReason, boolean finished) {
Log.e("onCameraPositionChanged"," cameraPosition: "+cameraPosition+" cameraUpdateReason: "+cameraUpdateReason+" finished: "+finished);
}
#Override
public void onMapTap(#NonNull Map map, #NonNull Point point) {
MapObjectCollection mapObjects = binding.mapview.getMap().getMapObjects();
mapObjects.clear();
PlacemarkMapObject placemarkMapObject = mapObjectCollection.addPlacemark(new Point(point.getLatitude(), point.getLongitude()),
ImageProvider.fromResource(this, R.mipmap.marker_flag));
submitQueryByPoint(point);
Log.e("onMapTap", "point lat - lang: " + point.getLatitude() + " : " + point.getLongitude());
}
#Override
public void onMapLongTap(#NonNull Map map, #NonNull Point point) {
Log.e("onMapLongTap","onMapLongTap");
}
#Override
public void onSearchResponse(#NonNull Response response) {
try {
Log.e("Search", "Response: " + response);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
#Override
public void onSearchError(#NonNull Error error) {
String errorMessage = "unknown_error_message";
if (error instanceof RemoteError) {
errorMessage = "remote_error_message";
} else if (error instanceof NetworkError) {
errorMessage = "network_error_message";
}
Log.e("Response error", " error: " + errorMessage);
}
}
In the onMapTap method, I get the tapped point coordinates and send a search request by point
#Override
public void onMapTap(#NonNull Map map, #NonNull Point point) {
MapObjectCollection mapObjects = binding.mapview.getMap().getMapObjects();
mapObjects.clear();
PlacemarkMapObject placemarkMapObject = mapObjectCollection.addPlacemark(new Point(point.getLatitude(), point.getLongitude()),
ImageProvider.fromResource(this, R.mipmap.marker_flag));
submitQueryByPoint(point);
Log.e("onMapTap", "point lat - lang: " + point.getLatitude() + " : " + point.getLongitude());
}
**Response is always null values. What I do wrong?
This is a GitHub whole project for this example https://github.com/davmehrabyan/YandexMapSearch **
CODE
public class YellowFragment extends Fragment implements FlowerAdapter.FlowerClickListener{
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_yellow, container, false);
swipeContainer_yellow = (SwipeRefreshLayout) view.findViewById(R.id.swipeContainer_yellow);
setRetainInstance(true);
mReferSharedPreference = new ReferSharedPreference(getContext());
mlat = mReferSharedPreference.getValue("Lat", "None");
mlon = mReferSharedPreference.getValue("Lon", "None");
mCoordinatesTextLinear = (TextView) view.findViewById(R.id.tv_coordinates_linear);
if(!mlat.equals("None")){
getLinearPosts();
}
else {
Toast.makeText(getContext(), "Choose your location", Toast.LENGTH_LONG).show();
}
swipeContainer_yellow.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
if(!mlat.equals("None")){
getLinearPosts();
}
else {
Toast.makeText(getContext(), "Choose your location", Toast.LENGTH_LONG).show();
}
}
});
configViews(view);
return view;
}
private void getLinearPosts() {
ReferSharedPreference preferenceCoordinates = new ReferSharedPreference(getContext());
String lat = preferenceCoordinates.getValue("Lat", "None");
String lon = preferenceCoordinates.getValue("Lon", "None");
mCoordinatesTextLinear.setText("Your Location :" + lat + " , " + lon);
mRestManager = new RestManager();
Call<List<Flower>> listCall = mRestManager.getmFlowerApiService(getActivity()).getAllFlowers(lat, lon);
listCall.enqueue(new Callback<List<Flower>>() {
#Override
public void onResponse(Call<List<Flower>> call, Response<List<Flower>> response) {
if (response.isSuccessful()) {
mFlowerAdapter.clear();
List<Flower> flowerList = response.body();
for(int i =0; i<flowerList.size(); i++) {
Flower flower = flowerList.get(i);
mFlowerAdapter.addFlower(flower);
}
swipeContainer_yellow.setRefreshing(false);
}
}
#Override
public void onFailure(Call<List<Flower>> call, Throwable t) {
}
});
}
In this code, when i swipe, if mlat is None, there is swipe updating icon and it's not disappear.
What i wanted is that if mlat has some value, do getLinearPosts();, but if mlat has None, show Toast "Choose your location".
but refershing icon do not disappear
like this
Question: How can i disappear this updating or refreshing icon when mlat is None?
After your task is completed, just call swipeContainer_yellow.setRefreshing(false) and it will hide that progressbar
As i can see you have already used it, but problem is you are assuming that your response will be perfect all the time. So instead of keeping it inside if (response.isSuccessful()), keep it outside if condition and also put it in onFailure()
listCall.enqueue(new Callback<List<Flower>>() {
#Override
public void onResponse(Call<List<Flower>> call, Response<List<Flower>> response) {
swipeContainer_yellow.setRefreshing(false);
if (response.isSuccessful()) {
mFlowerAdapter.clear();
List<Flower> flowerList = response.body();
for(int i =0; i<flowerList.size(); i++) {
Flower flower = flowerList.get(i);
mFlowerAdapter.addFlower(flower);
}
}
}
#Override
public void onFailure(Call<List<Flower>> call, Throwable t) {
swipeContainer_yellow.setRefreshing(false);
}
});
I am displaying a set of marker in my Google Map and I want to make the makerss clickable only once. If I click on the same marker again I should get a message like "already clicked" or something like that. How can i do it?
public void getData() {
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(DATA_URL,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
parseData(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
//Creating request queue
RequestQueue requestQueue = Volley.newRequestQueue(this);
//Adding request to the queue
requestQueue.add(jsonArrayRequest);
}
private void parseData(JSONArray array) {
for (int i = 0; i < array.length(); i++) {
JSONObject json = null;
try {
json = array.getJSONObject(i);
place1 = json.getString("place");
Lat1 = json.getString("latitude");
Long1 = json.getString("longitude");
sensor = json.getString("sensor1");
bin_capacity = json.getString("capacity");
if (Integer.parseInt(sensor) >= 0 && Integer.parseInt(sensor) <= 60) {
LatLng latLng = new LatLng(Double.parseDouble(Lat1), Double.parseDouble(Long1));
arrayLatlong.add(latLng);
for (int j = 0; j < arrayLatlong.size(); j++) {
arrayPlace.add(place1);
bin.add(bin_capacity);
myMarker= map.addMarker(new MarkerOptions().position(latLng).title(bin_capacity));
arrayMarker.add(myMarker);
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
for (int k = 0; k < arrayMarker.size(); k++) {
if (marker.equals(arrayMarker.get(k))) {
counter++;
capacity.setText("Capacity = "+counter);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Use boolean to maintain the state :
boolean isMarkerClicked = false;
And update in onclicklistener
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
if(!isMarkerClicked){
isMarkerClicked = true;
//put your rest of code that will work on marker click
}else{
//show here toast message
}
}
});
try with this
on marker click listener this code
googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
//Toast.makeText(MapActivity.this, marker.getTitle(), Toast.LENGTH_SHORT).show();// display toast
return false;
}
});
// marker title and messages clickable use this code
googleMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker marker) {
if (marker.getTitle().equals("Your Location")) {
} else {
Toast.makeText(MapActivity.this, map.get("" + marker_list.indexOf(marker.getTitle())).get("stylistUserId"), Toast.LENGTH_SHORT).show();
}
}
});
In this case what you can do is create a Temporary ArrayList() or HashMap().
Inside your onMarkerClick() method check if our temp list contains the marker, if yes show the toast and if not add the marker to temp list.
Replace folowing code:
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
for (int k = 0; k < arrayMarker.size(); k++) {
if (marker.equals(arrayMarker.get(k))) {
counter++;
capacity.setText("Capacity = " + counter);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
with this:
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
if (arrayMarker.contains(marker)) {
//show here toast message
} else {
arrayMarker.add(marker);
//put your rest of code that will work on marker click
}
}
}
I am trying to visualize multiple markers on map. Its showing perfectly and onMarkerTap its showing information retrieved from database as JSON. What I want to do is showing the information in a custom layout or bottom sheet. When user tap on marker a bottom sheet will appear with the information related to that marker. I am using the following code for the showing of multiple markers. here on MapViewListener section onTapMarker I have set the marker.getTitle() to show the marker name in the Toast for test. But it is showing the same marker name for all markers. But in the infoWindow built in that show on map showing accurate data. How can I solve this?
FloatingActionButton layerButton = (FloatingActionButton)findViewById(R.id.layer);
layerButton.setOnClickListener(new View.OnClickListener() {
public void getData() {
String url = Config.DATA_URL;
StringRequest stringRequest = new StringRequest(url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
showJSON(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this,error.getMessage().toString(),Toast.LENGTH_LONG).show();
}
});
requestQueue.add(stringRequest);
}
public void showJSON(String response){
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray jsonArray = jsonObject.getJSONArray(Config.JSON_ARRAY);
for (int i=0;i<jsonArray.length();i++){
JSONObject singleMarker = jsonArray.getJSONObject(i);
String poi_name = singleMarker.getString(Config.POI_NAME);
String poi_latitude = singleMarker.getString(Config.POI_LATITUDE);
String poi_longitude = singleMarker.getString(Config.POI_LONGITUDE);
Double dbl_latitude = Double.parseDouble(poi_latitude);
Double dbl_longitude = Double.parseDouble(poi_longitude);
final Marker marker = new Marker(poi_name, poi_thananame, new LatLng(dbl_latitude, dbl_longitude));
marker.setMarker(getResources().getDrawable(R.mipmap.poi_shopping));
mapView.addMarker(marker);
mapView.setMapViewListener(new MapViewListener() {
#Override
public void onShowMarker(MapView pMapView, Marker pMarker) {
}
#Override
public void onHideMarker(MapView pMapView, Marker pMarker) {
}
#Override
public void onTapMarker(MapView pMapView, Marker pMarker) {
Toast.makeText(getApplicationContext(), " Name: "+marker.getTitle()+, Toast.LENGTH_LONG).show();
}
#Override
public void onLongPressMarker(MapView pMapView, Marker pMarker) {
}
#Override
public void onTapMap(MapView pMapView, ILatLng pPosition) {
}
#Override
public void onLongPressMap(MapView pMapView, ILatLng pPosition) {
}
});
}
} catch (JSONException e) {
e.printStackTrace();
}
//
// mapView.setCenter(new LatLng(dbl_latitude, dbl_longitude));
// mapView.setZoom(18);
}
//////END OF GET DATA///////
#Override
public void onClick(View v) {
clearLayerFAB.setVisibility(View.VISIBLE);
getData();
}
});
happy to help you out with this one. I assume you are using the 3.2.0 version of the Mapbox Android SDK. If so, I see two problems with your code posted above.
1) You are setting up your listener within the for loop so every time you add a marker your just reseting the listener.
2) Both 3.2.0 and the newer 4.0.0 have a setOnMarkerClickListener method you can call and within it you can add your toast. So it will looks something like this:
for (int i=0;i<jsonArray.length();i++){
// Add your markers here
}
// Setup your listener here
mapView.setOnMarkerClickListener(new MapboxMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(#NonNull Marker marker) {
Toast.makeText(getApplicationContext(), " Name: "+marker.getTitle()+, Toast.LENGTH_LONG).show();
return false;
}
});
Hopefully this helps!
I am following an Android weather app tutorial where the weather app requests a wether information about a place using an API;
However, the app was not "location aware" so I decided to try to do that, but when my app is created it calls the API with the hardcoded coordinates (private latitude and longitude). I dont understand why thid is happening, I am calling the getLoation method before the getForecast method. getLocation method should use the location manager's internet provider and set the latitude and longitude before the getForecast in called (which uses the coordinates to make the asynchronous API call). The strange thing is that when I press the refresh button, the location's coordinates are obtained from the location manager and the getLocationName is working fine too (finds the name of the location using the coordinates and set's the locationLabel to the name).
I suspect e problem is because if the fact that OkHTTP API that I am using to make the asynchronous call uses worker thread.
NODE: I am using Butter knife and YoYo API's also I have four classes which store the information about the weather called Forecast, Hour and Current and Day. I did not include them because I thought it is not important, since the problem is in my main activity class
Here is the code in my main activity class:
public static final String TAG = MainActivity.class.getSimpleName();
private Forecast mForecast;
//default coordinates - Aberdeen, UK Lati:57.156866 ; Long:
private double latitude = 57.156866;
private double longitude = -2.094278;
private LocationManager locationManager;
#InjectView(R.id.timeLabel) TextView mTimeLabel;
#InjectView(R.id.temperatureLabel) TextView mTemperatureLabel;
#InjectView(R.id.humidityValue) TextView mHumidityValue;
#InjectView(R.id.precipValue) TextView mPrecipValue;
#InjectView(R.id.summaryLabel) TextView mSummaryLabel;
#InjectView(R.id.locationLabel) TextView mLocationLabel;
#InjectView(R.id.windSpeedValue) TextView mWindSpeedValue;
#InjectView(R.id.iconImageView) ImageView mIconImageView;
#InjectView(R.id.refreshImageView) ImageView mRefreshImaveView;
#InjectView(R.id.progressBar) ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImaveView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getLocation();
getForecast(latitude, longitude);
}
});
getLocation();
getForecast(latitude, longitude);
}
#Override
protected void onResume() {
super.onResume();
getForecast(latitude, longitude);
}
private void getForecast(double latitude, double longitude) {
//animations
YoYo.with(Techniques.FadeIn).duration(1800).playOn(mLocationLabel);
YoYo.with(Techniques.FadeIn).duration(1600).playOn(mTemperatureLabel);
YoYo.with(Techniques.FadeIn).duration(1800).playOn(mIconImageView);
YoYo.with(Techniques.FadeIn).duration(1000).playOn(mSummaryLabel);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mHumidityValue);
YoYo.with(Techniques.FadeIn).duration(1400).playOn(mWindSpeedValue);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mPrecipValue);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mTimeLabel);
String API_KEY = "API_KEY";
String forecast = "https://api.forecast.io/forecast/"+ API_KEY +"/"+ latitude+","+ longitude+"?units=auto";
if(isNetworkAvailable()) {
toggleRefresh();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(forecast)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
alertUserAboutError();
}
//when the call to the Okhttp library finishes, than calls this method:
#Override
public void onResponse(Response response) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
try {
String jsonData = response.body().string();
//Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mForecast = parseForecastDetails(jsonData);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateDisplay();
}
});
} else {
alertUserAboutError();
}
} catch (IOException | JSONException e) {
Log.e(TAG, "Exception caught:", e);
}
}
});
}else{
//Toast.makeText(this,getString(R.string.network_unavailable_message),Toast.LENGTH_LONG).show();
WIFIDialogFragment dialog = new WIFIDialogFragment();
dialog.show(getFragmentManager(), getString(R.string.error_dialog_text));
}
}
private void toggleRefresh() {
if(mProgressBar.getVisibility() == View.INVISIBLE){
mProgressBar.setVisibility(View.VISIBLE);
mRefreshImaveView.setVisibility(View.INVISIBLE);
}else{
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImaveView.setVisibility(View.VISIBLE);
}
}
//updates the dysplay with the data in the CUrrentWeather locaal object
private void updateDisplay() {
Current current = mForecast.getCurrent();
//setting the current weather details to the ui
mTemperatureLabel.setText(current.getTemperature()+"");
mTimeLabel.setText("At "+ current.getFormattedTime()+" it will be");
mHumidityValue.setText(current.getHumidity() +"%");
mPrecipValue.setText(current.getPrecipChange()+"%");
mSummaryLabel.setText(current.getSummery());
mWindSpeedValue.setText(current.getWindSpeed()+"");
mLocationLabel.setText(current.getTimeZone());
//sets the mLocationLavel to the appropriate name and not the timezome from the API
getLocationName();
Drawable drawable = ContextCompat.getDrawable(this, current.getIconId());
mIconImageView.setImageDrawable(drawable);
}
private Forecast parseForecastDetails(String jsonData) throws JSONException {
Forecast forecast = new Forecast();
forecast.setCurrent(getCurrentDetails(jsonData));
forecast.setHourlyForecast(getHourlyForecast(jsonData));
forecast.setDailyForecast(getDailyForecast(jsonData));
return forecast;
}
private Day[] getDailyForecast(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject daily = forecast.getJSONObject("daily");
JSONArray data = daily.getJSONArray("data");
Day[] days = new Day[data.length()];
for(int i = 0;i < data.length();i++){
JSONObject jsonDay = data.getJSONObject(i);
Day day = new Day();
day.setSummary(jsonDay.getString("summary"));
day.setIcon(jsonDay.getString("icon"));
day.setTemperatureMax(jsonDay.getDouble("temperatureMax"));
day.setTime(jsonDay.getLong("time"));
day.setTimezone(timezone);
days[i] = day;
Log.v(MainActivity.class.getSimpleName(),days[i].getIcon());
}
return days;
}
private Hour[] getHourlyForecast(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject hourly = forecast.getJSONObject("hourly");
JSONArray data = hourly.getJSONArray("data");
Hour[]hours = new Hour[data.length()];
for(int i = 0;i < data.length();i++){
JSONObject jsonHour = data.getJSONObject(i);
Hour hour = new Hour();
hour.setSummary(jsonHour.getString("summary"));
hour.setTemperature(jsonHour.getDouble("temperature"));
hour.setIcon(jsonHour.getString("icon"));
hour.setTime(jsonHour.getLong("time"));
hour.setTimezone(timezone);
hours[i] = hour;
}
return hours;
}
/*
* throws JSONException, doing it like that, we place the
* responsability of handaling this exeption to the caller of the method
*/
private Current getCurrentDetails(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
Log.i(TAG,"From JSON: " + timezone);
JSONObject currently = forecast.getJSONObject("currently");
Current mCurrent = new Current();
mCurrent.setHumidity(currently.getDouble("humidity"));
mCurrent.setTime(currently.getLong("time"));
mCurrent.setIcon(currently.getString("icon"));
mCurrent.setPrecipChange(currently.getDouble("precipProbability"));
mCurrent.setSummery(currently.getString("summary"));
mCurrent.setTemperature(currently.getDouble("temperature"));
mCurrent.setTimeZone(timezone);
mCurrent.setWindSpeed(currently.getDouble("windSpeed"));
Log.d(TAG, mCurrent.getFormattedTime());
return mCurrent;
}
private boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
//contition to check if there is a network and if the device is connected
if(networkInfo != null && networkInfo.isConnected()){
isAvailable = true;
}
return isAvailable;
}
private void alertUserAboutError() {
AlertDIalogFragment dialog = new AlertDIalogFragment();
dialog.show(getFragmentManager(),getString(R.string.error_dialog_text));
}
private void getLocation(){
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
if(isNetworkAvailable()){
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1, 1000, new MyLocationListener());
}else{
WIFIDialogFragment dialog = new WIFIDialogFragment();
dialog.show(getFragmentManager(), getString(R.string.error_dialog_text));
}
}
private class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location loc) {
latitude = loc.getLatitude();
longitude = loc.getLongitude();
Toast.makeText(MainActivity.this,
"Location changed: Lat: " + loc.getLatitude() + " Lng: "
+ loc.getLongitude(), Toast.LENGTH_SHORT).show();
locationManager.removeUpdates(this);
}
#Override
public void onProviderDisabled(String provider) {}
#Override
public void onProviderEnabled(String provider) {}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
private void getLocationName(){
Geocoder geo = new Geocoder(this, Locale.getDefault());
try {
List<Address> addressList = geo.getFromLocation(this.latitude,this.longitude,1);
if (addressList.isEmpty()){
//gets the default name from the timeZone
//that we set in as a local variable
}else{
if(addressList.size() > 0){
Log.v(MainActivity.class.getSimpleName(),addressList.get(0).getLocality() + ", "+ addressList.get(0).getCountryName()+"");
mLocationLabel.setText(addressList.get(0).getLocality() + ", "+ addressList.get(0).getCountryName());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
This is a screenshot of the app:
By looking at your code, there are couple of things which are wrong.
you have called getLocation Method in OnCreate and onResume as well which is logically incorrect. you can keep it at onResume only if you wish you get location frequently.
when we call getLocation method it's not guarantee to give the latitude and longitude straight away, it takes it's own sweet time depending on the providers (Read API of location manager) hence your first call for getForecast will probably fail.
Solution : you can call getForecast method when onLocationChange method is called, by that time you can show progressDialog.
Make sure you have given Location related permissions in the manifest file