Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android Build a Weather App (2015) Hooking Up the Model to the View Wrapping Up

Juan Francisco Andrade Álvarez
Juan Francisco Andrade Álvarez
23,997 Points

Added location to the Stormy App, but the new location coordinates are only applied after clicking the refresh button.

Using the knowledge from https://github.com/treehouse/android-location-example-refactored by Ben Jakuben i was able to make Stormy location awareness. However, when launching the App, the location coordinates are not fetched. Latitude and longitud get a 0.0 value and Stormy fetches the forecast for such coordinates. But after clicking the Stormy refresh button, the location is gotten and Stormy shows the right forecast.

I have defined 2 member variables: private double mCurrentLatitude; private double mCurrentLongitude;

to make them available throughout the MainActivity class. I just have overridden the handleNewLocation() method from LocationProvider.LocationCallback to get the new coordinates:

mCurrentLatitude = location.getLatitude(); mCurrentLongitude = location.getLongitude();

In the getForecast() method i'm now replacing the Alcatraz hard coded coordinates by these member variables, which are supposed to be available after the callback in handleNewLocation(). And finally in the onCreate() and the onResume() methods i'm calling: mLocationProvider.connect(); getForecast();

Is there a way to fetch the location just after launching the app? Or, should i hard code a default location, such as the 0.0 latitude, longitude?

Thanks for the help and for such amazing series of courses in the Android track!!

5 Answers

Just delete getForecast() in OnCreate() and put it in handleNewLocation(). This way you will only get the forecast when the location is already set or you pressed refresh button.

Set the LocationRequests on some reasonable time and you wont have to worry about too many getForecast() calls.

Hello,

Without seeing your code, I can only offer some general advice that may or may not help. What I would recommend trying is in your Activity's onCreate method, to determine your location early in the method, that way it is then filling in the data to start with the current location. If you need more help, please post the code in question.

Juan Francisco Andrade Álvarez
Juan Francisco Andrade Álvarez
23,997 Points

The following is the code in MainActivity. The code from LocationProvider is basically the same as: https://github.com/treehouse/android-location-example-refactored/blob/master/app/src/main/java/teamtreehouse/com/iamhere/LocationProvider.java I have just tried to retrieve the location in the onCreate method, but it didn't solve the issue. Hope the code helps to visualise it better. Thanks again for your responses.

public class MainActivity extends ActionBarActivity implements LocationProvider.LocationCallback{
    /*some code omitted: members used in Stormy*/
    private double mCurrentLatitude;
    private double mCurrentLongitude;

      /*some code omitted: Activty views initialization*/

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.inject(this);
        mProgressBar.setVisibility(View.INVISIBLE);
        mRefreshImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getForecast();
            }
        });
        mLocationProvider = new LocationProvider(this,this);
        mLocationProvider.connect();
        Log.d(TAG,"lati"+mCurrentLatitude);
        getForecast();


    }
    @Override
    protected void onResume(){
        super.onResume();
        mLocationProvider.connect();
        Log.d(TAG, "latitude.." + mCurrentLatitude);
        getForecast();
    }
    protected void onPause(){
        super.onPause();
        mLocationProvider.disconnect();
    }

    private void getForecast() {
        String baseUrl = getString(R.string.base_url);
        String apiKey = getString(R.string.api_key);

        /*replacing Alcatraz location by new coordinates from Location Services*/

        //String latitude = getString(R.string.latitude);
        //String longitude = getString(R.string.longitude);
        String latitude = String.valueOf(mCurrentLatitude);
        String longitude = String.valueOf(mCurrentLongitude);

        String url = baseUrl+apiKey+"/"+latitude+","+longitude;
        Log.d(TAG,"forecast call..."+url);

      if(isNetworkAvailable()) {
            toggleRefresh();
            OkHttpClient client = new OkHttpClient();
            final Request request = new Request.Builder()
                    .url(url)
                    .build();
            Call call = client.newCall(request);

            call.enqueue(new Callback() {
                @Override
                public void onFailure(Request request, IOException e) {
                    Log.d(TAG, request.url().toString(), e);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            toggleRefresh();
                        }
                    });
                }

                @Override
                public void onResponse(Response response) throws IOException {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            toggleRefresh();
                        }
                    });
                    try {
                        String jsonData = response.body().string();

                        if (response.isSuccessful()) {
                            mCurrentWeather = getCurrentDetails(jsonData);
                            //send message to main thread
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {

                                    updateDisplay();
                                }
                            });


                        } else {
                            alertUserAboutError();
                        }

                    } catch (IOException e) {
                        Log.e(TAG, "fail...", e);
                    } catch (JSONException e) {
                        Log.e(TAG, "fail...json", e);
                    }
                }
            });

        }else{
            Toast.makeText(this, "Network is unavailable", Toast.LENGTH_LONG).show();
        }
    }

    private void toggleRefresh() {
        if(mProgressBar.getVisibility()==View.INVISIBLE) {
            mProgressBar.setVisibility(View.VISIBLE);
            mRefreshImageView.setVisibility(View.INVISIBLE);
        }else {
            mProgressBar.setVisibility(View.INVISIBLE);
            mRefreshImageView.setVisibility(View.VISIBLE);
        }
    }

    private void updateDisplay() {
        mTemperatureLabel.setText(mCurrentWeather.getTemperature()+"");
        mTimeLabel.setText("At "+mCurrentWeather.getFormattedTime()+" it will be:");
        mHumidityValue.setText(mCurrentWeather.getHuminity()+"");
        mPrecipValue.setText(mCurrentWeather.getPrecipChance() + " %");
        mSummaryLabel.setText(mCurrentWeather.getSummary());
        Drawable drawable = getResources().getDrawable(mCurrentWeather.getIconId());
        mIconImageView.setImageDrawable(drawable);
    }

    private CurrentWeather getCurrentDetails(String jsonData) throws JSONException{
        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");
        JSONObject currently = forecast.getJSONObject("currently");

        String icon = currently.getString("icon");
        long time = currently.getLong("time");
        double humidity = currently.getDouble("humidity");
        double precipChance = currently.getDouble("precipProbability");
        double temperature = currently.getDouble("temperature");
        String summary = currently.getString("summary");

        CurrentWeather currentWeather = new CurrentWeather();

        currentWeather.setHuminity(humidity);
        currentWeather.setIcon(icon);
        currentWeather.setPrecipChance(precipChance);
        currentWeather.setTemperature(temperature);
        currentWeather.setSummary(summary);
        currentWeather.setTime(time);
        currentWeather.setTimeZone(timezone);

        Log.d(TAG,currentWeather.getFormattedTime());
        return currentWeather;
    }

    private boolean isNetworkAvailable() {
        ConnectivityManager connectivityManager = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

        boolean isAvailable = false;


        if(networkInfo!=null && networkInfo.isConnected()){
            isAvailable = true;
        }

        return isAvailable;
    }

    private void alertUserAboutError() {

        AlertDialogFragment alertDialogFragment = new AlertDialogFragment();

        alertDialogFragment.show(getFragmentManager(),"weather_api_error");
    }


    @Override
    public void handleNewLocation(Location location) {
        Log.d(TAG, "handling new loc..."+location.toString());
        /*Setting new location coordinates*/
        mCurrentLatitude = location.getLatitude();
        mCurrentLongitude = location.getLongitude();

    }

Hello,

One option is putting a getForecast() call after you update the mCurrentLatitude and mCurrentLongitude in your handleNewLocation() method. This should update the forcast with your current location immediately when your device detects that you are somewhere different than it is given. Though, depending upon how often the location is "new" this could be doing too many getForecast() calls.

Juan Francisco Andrade Álvarez
Juan Francisco Andrade Álvarez
23,997 Points

Sure it should work when location changes. However when app launches it just gets the 0.0 lat and long coordinates, not the current location ones. Maybe, does it have to be provided a default location?. For example i would just initialise the lat and long members in the onCreate method:

 mCurrentLatitude = -0.1496346;
 mCurrentLongitude = -78.4724142;

Though, i think it would not result, obviously, in a good user experience.

You could always store the last lat and long in the sharedpreferences and load the value from there when you start the app. Its not too far of a stretch to think that they might be close to the last location that they checked the app. At which point, the only time they'd have to deal with a "bad" default would be the very first time they ever open the app.

Juan Francisco Andrade Álvarez
Juan Francisco Andrade Álvarez
23,997 Points

Ok. Thanks. It's clearer for me now. I'll keep on improving this behaviour, but i think the basics are covered.

I'm trapped on the same problem as you, how did you end up figuring it out ?

Juan Francisco Andrade Álvarez
Juan Francisco Andrade Álvarez
23,997 Points

Not yet. I still have to investigate a little further. Also if you have any idea, share it please.

Andre Colares
Andre Colares
5,437 Points

I hard coded the latitude and longitude (I'm at São Paulo / BRAZIL)... Here is my code and the location is appearing since the beggining:

package com.andrecolares.stormy;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

import butterknife.Bind;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    @Bind(R.id.timeLabel)TextView mTimeLabel;
    @Bind(R.id.temperatureLabel) TextView mTemperatureLabel;
    @Bind(R.id.humidityValue) TextView mHumidityValue;
    @Bind(R.id.precipValue) TextView mPrecipValue;
    @Bind(R.id.summaryLabel) TextView mSummaryLabel;
    @Bind(R.id.iconImageView) ImageView mIconImageView;
    @Bind(R.id.refreshImageView) ImageView mRefreshImageView;
    @Bind(R.id.progressBar) ProgressBar mProgressBar;
    @Bind(R.id.locationLabel) TextView mTimeZone;

    private CurrentWeather mCurrentWeather;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        mProgressBar.setVisibility(View.INVISIBLE);

        final double latitude = -25.4167;//37.8267;
        final double longitude = -49.2500;//-122.423;

        mRefreshImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getForecast(latitude, longitude);

            }
        });

        getForecast(latitude, longitude);

        Log.d(TAG, "Main UI code is running!");
    }

    private void getForecast(double latitude, double longitude) {
        String apiKey = "b58df3c3139b75263e389c950140c157";

        String forecastUrl = "https://api.forecast.io/forecast/" + apiKey + "/" + latitude +"," + longitude;

        if (isNetworkAvaible()) {
            toggleRefresh();

            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url(forecastUrl)
                    .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();
                }

                @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()) {
                            mCurrentWeather = getCurrentDetails(jsonData);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    updateDisplay();
                                }
                            });

                        } else {
                            alertUserAboutError();
                        }
                    } catch (IOException e) {
                        Log.e(TAG, "Exception caught: ", e);
                    }
                    catch (JSONException e){
                        Log.e(TAG, "Exception caught: ", e);
                    }
                }
            });
        }else{
            Toast.makeText(this, R.string.network_unavaible_message, Toast.LENGTH_LONG).show();
        }
    }

    private void toggleRefresh() {
        if (mProgressBar.getVisibility() == View.INVISIBLE){
            mProgressBar.setVisibility(View.VISIBLE);
            mRefreshImageView.setVisibility(View.INVISIBLE);

        }else {
            mProgressBar.setVisibility(View.INVISIBLE);
            mRefreshImageView.setVisibility(View.VISIBLE);
        }

    }

    private void updateDisplay() {
        mTimeZone.setText(mCurrentWeather.getTimeZone());
        mTemperatureLabel.setText(mCurrentWeather.getTemperature() + "");
        mTimeLabel.setText("At " + mCurrentWeather.getFormattedTime() + " it will be");
        mHumidityValue.setText(mCurrentWeather.getHumidity() + "");
        mPrecipValue.setText(mCurrentWeather.getPrecipChance() + "%");
        mSummaryLabel.setText(mCurrentWeather.getSummary());

        Drawable drawable = getResources().getDrawable(mCurrentWeather.getIconId());
        mIconImageView.setImageDrawable(drawable);
    }

    private CurrentWeather 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");

        CurrentWeather currentWeather = new CurrentWeather();
        currentWeather.setHumidity(currently.getDouble("humidity"));
        currentWeather.setTime(currently.getLong("time"));
        currentWeather.setIcon(currently.getString("icon"));
        currentWeather.setPrecipChance(currently.getDouble("precipProbability"));
        currentWeather.setSummary(currently.getString("summary"));
        currentWeather.setTemperature(currently.getDouble("temperature"));
        currentWeather.setTimeZone(timezone);

        Log.d(TAG,currentWeather.getFormattedTime());

        return currentWeather;

    }

    private void alertUserAboutError() {
        AlertDialogFragment dialog = new AlertDialogFragment();
        dialog.show(getFragmentManager(), "error_dialog");
    }

    public boolean isNetworkAvaible() {
        android.net.ConnectivityManager manager = (android.net.ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        boolean isAvaible = false;
        if(networkInfo != null && networkInfo.isConnected()){
            isAvaible = true;
        }
        return isAvaible;
    }
}
Andre Colares
Andre Colares
5,437 Points

Here is the Current Weather class where is set the Timezone:

package com.andrecolares.stormy;


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class CurrentWeather {
    private String mIcon;
    private long mTime;
    private double mTemperature;
    private double mHumidity;
    private double mPrecipChance;
    private String mSummary;
    private String mTimeZone;


    public String getTimeZone() {
        return mTimeZone;
    }

    public void setTimeZone(String timeZone) {
        mTimeZone = timeZone;
    }

    public String getIcon() {
        return mIcon;
    }

    public void setIcon(String icon) {
        mIcon = icon;
    }

    public int getIconId(){
        //
        int iconId = R.drawable.clear_day;

        if (mIcon.equals("clear-day")) {
            iconId = R.drawable.clear_day;
        }
        else if (mIcon.equals("clear-night")) {
            iconId = R.drawable.clear_night;
        }
        else if (mIcon.equals("rain")) {
            iconId = R.drawable.rain;
        }
        else if (mIcon.equals("snow")) {
            iconId = R.drawable.snow;
        }
        else if (mIcon.equals("sleet")) {
            iconId = R.drawable.sleet;
        }
        else if (mIcon.equals("wind")) {
            iconId = R.drawable.wind;
        }
        else if (mIcon.equals("fog")) {
            iconId = R.drawable.fog;
        }
        else if (mIcon.equals("cloudy")) {
            iconId = R.drawable.cloudy;
        }
        else if (mIcon.equals("partly-cloudy-day")) {
            iconId = R.drawable.partly_cloudy;
        }
        else if (mIcon.equals("partly-cloudy-night")) {
            iconId = R.drawable.cloudy_night;
        }
        return iconId;
    }

    public long getTime() {
        return mTime;
    }
    //Formatar o modelo do texto das horas
    public String getFormattedTime(){
        SimpleDateFormat formatter = new SimpleDateFormat("h: mm a");
        formatter.setTimeZone(TimeZone.getTimeZone(getTimeZone()));
        Date dateTime = new Date(getTime()*1000);
        String timeString = formatter.format(dateTime);

        return timeString;
    }

    public void setTime(long time) {
        mTime = time;
    }

    public int getTemperature() {
        return (int) Math.round(mTemperature);
    }

    public void setTemperature(double temperature) {
        mTemperature = temperature;
    }

    public double getHumidity() {
        return mHumidity;
    }

    public void setHumidity(double humidity) {
        mHumidity = humidity;
    }

    public int getPrecipChance() {
        double precipPercentage = mPrecipChance *100;
        return (int) Math.round(precipPercentage);
    }

    public void setPrecipChance(double precipChance) {
        mPrecipChance = precipChance;
    }

    public String getSummary() {
        return mSummary;
    }

    public void setSummary(String summary) {
        mSummary = summary;
    }
}