Create a beautiful Android Weather App using OpenWeatherMap API and Volley Library part 2

We are going to finish off where we stopped in part one of creating beautiful Android Weather App using OpenWeatherMap API and Volley Library.

If you has not read the part one I will suggest you first read it before you proceed with this tutorial.

android weather app tutorial

This is highlight of what we covered in part one.

1. Register with OpenWaetherMap.org and obtained a key for our API request call

2. We created a new android project name AndroidWaetherApp

3. We went further to talk about all the helper, adapter and other resources used in the application

In this tutorial, we will focus on other few classes we did not over and also we will focus on the Activity classes.

Create Entity Package

We will create a new folder package called entity. The entity folder will contain Java bean like classes. Create the following classes in the folder and add the code below to each class.

private int id;

DatabaseLocationObject.java

public class DatabaseLocationObject {
    private int id;
    private String location;
    public DatabaseLocationObject(int id, String location) {
        this.id = id;
        this.location = location;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getLocation() {
        return location;
    }
    public void setLocation(String location) {
        this.location = location;
    }
}

ListJsonObject.java

import com.google.gson.annotations.SerializedName;
import com.inducesmile.androidweatherapp.entity.Coord;
public class ListJsonObject {
    @SerializedName("_id")
    private String _id;
    @SerializedName("name")
    private String name;
    @SerializedName("country")
    private String country;
    @SerializedName("coord")
    private Coord coord;
    public ListJsonObject(String _id, String name, String country, Coord coord) {
        this._id = _id;
        this.name = name;
        this.country = country;
        this.coord = coord;
    }
    public String get_id() {
        return _id;
    }
    public String getName() {
        return name;
    }
    public String getCountry() {
        return country;
    }
    public Coord getCoord() {
        return coord;
    }
}

LocationObject.java

    
public LocationObject(int id, String locationCity, String weatherInformation) {
        this.id = id;
        this.locationCity = locationCity;
        this.weatherInformation = weatherInformation;
    }
    public String getLocationCity() {
        return locationCity;
    }
    public String getWeatherInformation() {
        return weatherInformation;
    }
    public int getId() {
        return id;
    }
}

ViewEntityObject.java

import android.widget.RadioButton;
public class ViewEntityObject {
    private RadioButton radioButton;
    private String radioName;
    public ViewEntityObject(RadioButton radioButton, String radioName) {
        this.radioButton = radioButton;
        this.radioName = radioName;
    }
    public RadioButton getRadioButton() {
        return radioButton;
    }
    public void setRadioButton(RadioButton radioButton) {
        this.radioButton = radioButton;
    }
    public String getRadioName() {
        return radioName;
    }
    public void setRadioName(String radioName) {
        this.radioName = radioName;
    }
}

WeatherObject.java

public class WeatherObject {
    private String dayOfWeek;
    private int weatherIcon;
    private String weatherResult;
    private String weatherResultSmall;
    public WeatherObject(String dayOfWeek, int weatherIcon, String weatherResult, String weatherResultSmall) {
        this.dayOfWeek = dayOfWeek;
        this.weatherIcon = weatherIcon;
        this.weatherResult = weatherResult;
        this.weatherResultSmall = weatherResultSmall;
    }
    public String getDayOfWeek() {
        return dayOfWeek;
    }
    public int getWeatherIcon() {
        return weatherIcon;
    }
    public String getWeatherResult() {
        return weatherResult;
    }
    public String getWeatherResultSmall() {
        return weatherResultSmall;
    }
}

Create Json Package Folder

Since we are going to use Gson library to parse the server response in Json format, we are going to create Java object class that mimick the structure of the Json object.

We will create different java class, you can check the folder in the source code below and create and paste the code to the different classes.

Activity and Layout pages

Right now, we will focus on the Activity and layout files in our projects. We will start by creating the Splash intro screen of the app.

Splash Screen Activity Class

While the splash screen is load, we will read all the location in city.list.json file located in the assets folder and store the content on a database and a refer is kept in SharedPreference. The code contain of the class below.

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.Window;
import android.view.WindowManager;
import com.google.common.collect.Lists;
import com.google.common.math.IntMath;
import com.inducesmile.androidweatherapp.entity.ListJsonObject;
import com.inducesmile.androidweatherapp.helpers.CustomApplication;
import com.inducesmile.androidweatherapp.helpers.CustomSharedPreference;
import com.inducesmile.androidweatherapp.helpers.Helper;
import java.io.InputStream;
import java.math.RoundingMode;
import java.util.List;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private final int SPLASH_DISPLAY_LENGTH = 5000;
    private CustomSharedPreference customSharedPreference;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.hide();
        }
        customSharedPreference = new CustomSharedPreference(MainActivity.this);
        if(!customSharedPreference.getDataSourceIfPresent()){
            PrepareDataSource mDataSource = new PrepareDataSource();
            mDataSource.execute();
        }
        new Handler().postDelayed(new Runnable(){
            @Override
            public void run(){
                Intent startActivityIntent = new Intent(MainActivity.this, WeatherActivity.class);
                startActivity(startActivityIntent);
                MainActivity.this.finish();
            }
        }, SPLASH_DISPLAY_LENGTH);
    }
    private class PrepareDataSource extends AsyncTask<Void, Void, Void> {
        protected void onProgressUpdate() {
        }
        protected void onPostExecute() {
        }
        @Override
        protected Void doInBackground(Void... voids) {
            InputStream stream = ((CustomApplication)getApplication()).getJsonStream();
            List<ListJsonObject> storeSourceData = ((CustomApplication)getApplication()).readStream(stream);
            // store data in shared reference
            int partitionSize = IntMath.divide(storeSourceData.size(), 2, RoundingMode.UP);
            List<List<ListJsonObject>> partitions = Lists.partition(storeSourceData, partitionSize);
            List<ListJsonObject> firstListObject = partitions.get(0);
            List<ListJsonObject> secondListObject = partitions.get(1);
            customSharedPreference.setDataFromSharedPreferences(Helper.STORED_DATA_FIRST, firstListObject);
            customSharedPreference.setDataFromSharedPreferences(Helper.STORED_DATA_SECOND, secondListObject);
            customSharedPreference.setDataSourceIfPresent(true);
            return null;
        }
    }
}

The corresponding layout file of the splash intro activity is below. Copy and add it to your project.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/intro_bg"
    tools:context="com.inducesmile.androidweatherapp.MainActivity">
</RelativeLayout>

AddLocationActivity Class

The AddLocationActivity class contains an EditText box where users can search for a location and save it if they want weather information about the location. It also give you location text suggestions about locations.

import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.Toast;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.inducesmile.androidweatherapp.adapters.CustomArrayAdapter;
import com.inducesmile.androidweatherapp.database.DatabaseQuery;
import com.inducesmile.androidweatherapp.entity.ListJsonObject;
import com.inducesmile.androidweatherapp.helpers.CustomSharedPreference;
import com.inducesmile.androidweatherapp.helpers.Helper;
import java.util.List;
public class AddLocationActivity extends AppCompatActivity {
    private static final String TAG = AddLocationActivity.class.getSimpleName();
    private AutoCompleteTextView addLocation;
    private RequestQueue queue;
    private CustomArrayAdapter customAdapter;
    private static List<ListJsonObject> mData;
    private CustomSharedPreference mPreference;
    private DatabaseQuery databaseQuery;
    private DataSourceFromSharedPref dataSourceFromSharedPref;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_location);
        setTitle(Helper.MANAGER_LOCATION);
        if(getSupportActionBar() != null){
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
        mPreference = new CustomSharedPreference(AddLocationActivity.this);
        databaseQuery = new DatabaseQuery(AddLocationActivity.this);
        queue = Volley.newRequestQueue(this);
        addLocation = (AutoCompleteTextView) findViewById(R.id.new_location);
        Button goToLocationButton = (Button)findViewById(R.id.go_to_location_button);
        goToLocationButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent listLocationIntent = new Intent(AddLocationActivity.this, ListLocationActivity.class);
                startActivity(listLocationIntent);
            }
        });
        final Button addLocationButton = (Button)findViewById(R.id.add_location_button);
        addLocationButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String enteredLocation = addLocation.getText().toString();
                if (TextUtils.isEmpty(enteredLocation)) {
                    Toast.makeText(AddLocationActivity.this, Helper.LOCATION_ERROR_MESSAGE, Toast.LENGTH_LONG).show();
                    return;
                }
                // add this to the database
                int numOfLocationsStored = databaseQuery.countAllStoredLocations();
                Toast.makeText(AddLocationActivity.this, "Total count " + numOfLocationsStored, Toast.LENGTH_LONG).show();
                if(numOfLocationsStored <= 3){
                    databaseQuery.insertNewLocation(enteredLocation);
                }else{
                    Toast.makeText(AddLocationActivity.this, getString(R.string.stored_location), Toast.LENGTH_LONG).show();
                }
                Intent listLocationIntent = new Intent(AddLocationActivity.this, ListLocationActivity.class);
                startActivity(listLocationIntent);
            }
        });
    }
    private class DataSourceFromSharedPref extends AsyncTask<Void, Void, List<ListJsonObject>> {
        protected void onProgressUpdate() {
        }
        protected void onPostExecute(List<ListJsonObject> result) {
            if(null != result){
                mData = result;
                customAdapter = new CustomArrayAdapter(AddLocationActivity.this, R.layout.city_list, mData);
                addLocation.setAdapter(customAdapter);
                addLocation.setThreshold(1);
            }
        }
        @Override
        protected List<ListJsonObject> doInBackground(Void... voids) {
            return mergeStoredData();
        }
    }
    @Override
    protected void onResume() {
        super.onResume();
        if(null == mData){
            dataSourceFromSharedPref = new DataSourceFromSharedPref();
            dataSourceFromSharedPref.execute();
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(null != null){
            dataSourceFromSharedPref.cancel(true);
        }
    }
    private List<ListJsonObject> mergeStoredData(){
        List<ListJsonObject> firstObject = mPreference.getAllDataObject(Helper.STORED_DATA_FIRST);
        List<ListJsonObject> secondObject = mPreference.getAllDataObject(Helper.STORED_DATA_SECOND);
        return Lists.newArrayList(Iterables.concat(firstObject, secondObject));
    }
}

The corresponding layout file for this activity class is below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/colorBackground"
    tools:context="com.inducesmile.androidweatherapp.AddLocationActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="5"
        android:padding="@dimen/_16sdp"
        android:background="@color/colorPrimary"
        android:orientation="vertical">
        <AutoCompleteTextView
            android:id="@+id/new_location"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/location_hints"
            android:inputType="textPersonName"
            android:maxLines="1"
            android:padding="@dimen/_12sdp"
            android:textSize="@dimen/_12sdp"
            android:background="@drawable/bottom_border"
            android:textColor="@color/colorWhite"
            android:singleLine="true" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_10sdp"
            android:text="@string/location_example"
            android:layout_marginTop="@dimen/_4sdp"
            android:textColor="@color/colorWhite"/>
        <Button
            android:id="@+id/add_location_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_16sdp"
            android:textColor="@color/colorWhite"
            android:text="@string/add_new_location"
            android:background="@color/colorBackground"
            android:padding="@dimen/_12sdp"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="5"
        android:padding="@dimen/_16sdp"
        android:orientation="vertical">
        <Button
            android:id="@+id/go_to_location_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/_32sdp"
            android:layout_gravity="center"
            android:textColor="@color/colorWhite"
            android:text="@string/go_to_location"
            android:background="@color/colorPrimary"
            android:padding="@dimen/_12sdp"/>
    </LinearLayout>
</LinearLayout>

ListLocationActivity.java

In this page, we will list all the locations we have stored. We can delete or select each location at a time. Copy and paste the code below in this class.

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.inducesmile.androidweatherapp.adapters.LocationAdapter;
import com.inducesmile.androidweatherapp.database.DatabaseQuery;
import com.inducesmile.androidweatherapp.entity.DatabaseLocationObject;
import com.inducesmile.androidweatherapp.entity.LocationObject;
import com.inducesmile.androidweatherapp.helpers.Helper;
import com.inducesmile.androidweatherapp.json.LocationMapObject;
import java.util.ArrayList;
import java.util.List;
public class ListLocationActivity extends AppCompatActivity {
    private static final String TAG = ListLocationActivity.class.getSimpleName();
    private DatabaseQuery query;
    private List<DatabaseLocationObject> allLocations;
    private LocationObject locationObject;
    private LocationMapObject locationMapObject;
    private RequestQueue queue;
    private List<LocationObject> allData;
    private LocationAdapter locationAdapter;
    private RecyclerView locationRecyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_location);
        setTitle(Helper.LOCATION_LIST);
        queue = Volley.newRequestQueue(ListLocationActivity.this);
        allData = new ArrayList<LocationObject>();
        query = new DatabaseQuery(ListLocationActivity.this);
        allLocations = query.getStoredDataLocations();
        if(null != allLocations){
            for(int i = 0; i < allLocations.size(); i++){
                // make volley network call here
                System.out.println("Response printing " + allLocations.get(i).getLocation());
                requestJsonObject(allLocations.get(i));
            }
        }
        Toast.makeText(ListLocationActivity.this, "Count number of locations " + allLocations.size(), Toast.LENGTH_LONG).show();
        ImageButton addLocation = (ImageButton) findViewById(R.id.add_location);
        addLocation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent addLocationIntent = new Intent(ListLocationActivity.this, AddLocationActivity.class);
                startActivity(addLocationIntent);
            }
        });
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(ListLocationActivity.this);
        locationRecyclerView = (RecyclerView) findViewById(R.id.location_list);
        locationRecyclerView.setLayoutManager(linearLayoutManager);
    }
    private void requestJsonObject(final DatabaseLocationObject paramValue){
        String url ="http://api.openweathermap.org/data/2.5/weather?q="+paramValue.getLocation()+"&APPID=62f6de3f7c0803216a3a13bbe4ea9914&units=metric";
        StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d(TAG, "Response " + response);
                GsonBuilder builder = new GsonBuilder();
                Gson gson = builder.create();
                locationMapObject = gson.fromJson(response, LocationMapObject.class);
                if (null == locationMapObject) {
                    Toast.makeText(getApplicationContext(), "Nothing was returned", Toast.LENGTH_LONG).show();
                } else {
                    int rowId = paramValue.getId();
                    Long tempVal = Math.round(Math.floor(Double.parseDouble(locationMapObject.getMain().getTemp())));
                    String city = locationMapObject.getName() + ", " + locationMapObject.getSys().getCountry();
                    String weatherInfo = String.valueOf(tempVal) + "<sup>o</sup>, " + Helper.capitalizeFirstLetter(locationMapObject.getWeather().get(0).getDescription());
                    allData.add(new LocationObject(rowId, city, weatherInfo));
                    locationAdapter = new LocationAdapter(ListLocationActivity.this, allData);
                    locationRecyclerView.setAdapter(locationAdapter);
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d(TAG, "Error " + error.getMessage());
            }
        });
        queue.add(stringRequest);
    }
}

The corresponding layout file for this activity class is below.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorBackground"
    tools:context="com.inducesmile.androidweatherapp.ListLocationActivity">
    <ImageButton
        android:id="@+id/add_location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:layout_centerHorizontal="true"
        android:layout_alignParentTop="true"
        android:elevation="@dimen/_4sdp"
        android:layout_margin="@dimen/_24sdp"
        android:src="@drawable/cross"/>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/location_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/add_location"
        android:layout_centerHorizontal="true"
        android:orientation="vertical"
        android:scrollbars="none"/>
</RelativeLayout>

WeatherActivity.java

This is the main weather class that display all Weather information. Create a WeatherActivity.java file and add the code below to the file.

import android.Manifest;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.github.pavlospt.CircleView;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.inducesmile.androidweatherapp.adapters.RecyclerViewAdapter;
import com.inducesmile.androidweatherapp.database.DatabaseQuery;
import com.inducesmile.androidweatherapp.entity.WeatherObject;
import com.inducesmile.androidweatherapp.helpers.CustomSharedPreference;
import com.inducesmile.androidweatherapp.helpers.Helper;
import com.inducesmile.androidweatherapp.json.FiveDaysForecast;
import com.inducesmile.androidweatherapp.json.FiveWeathers;
import com.inducesmile.androidweatherapp.json.Forecast;
import com.inducesmile.androidweatherapp.json.LocationMapObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class WeatherActivity extends AppCompatActivity implements LocationListener {
    private static final String TAG = WeatherActivity.class.getSimpleName();
    private RecyclerView recyclerView;
    private RecyclerViewAdapter recyclerViewAdapter;
    private TextView cityCountry;
    private TextView currentDate;
    private ImageView weatherImage;
    private CircleView circleTitle;
    private TextView windResult;
    private TextView humidityResult;
    private RequestQueue queue;
    private LocationMapObject locationMapObject;
    private LocationManager locationManager;
    private Location location;
    private final int REQUEST_LOCATION = 200;
    private CustomSharedPreference sharedPreference;
    private String isLocationSaved;
    private DatabaseQuery query;
    private String apiUrl;
    private FiveDaysForecast fiveDaysForecast;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_weather);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.hide();
        }
        queue = Volley.newRequestQueue(this);
        query = new DatabaseQuery(WeatherActivity.this);
        sharedPreference = new CustomSharedPreference(WeatherActivity.this);
        isLocationSaved = sharedPreference.getLocationInPreference();
        cityCountry = (TextView)findViewById(R.id.city_country);
        currentDate = (TextView)findViewById(R.id.current_date);
        weatherImage = (ImageView)findViewById(R.id.weather_icon);
        circleTitle = (CircleView)findViewById(R.id.weather_result);
        windResult = (TextView)findViewById(R.id.wind_result);
        humidityResult = (TextView)findViewById(R.id.humidity_result);
        locationManager = (LocationManager) getSystemService(Service.LOCATION_SERVICE);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(WeatherActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
        } else {
            if(isLocationSaved.equals("")){
                // make API call with longitude and latitude
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 100, 2, this);
                if (locationManager != null) {
                    location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                    apiUrl = "http://api.openweathermap.org/data/2.5/weather?lat="+location.getLatitude()+"&lon="+location.getLongitude()+"&APPID=62f6de3f7c0803216a3a13bbe4ea9914&units=metric";
                    makeJsonObject(apiUrl);
                }
            }else{
                // make API call with city name
                String storedCityName = sharedPreference.getLocationInPreference();
                //String storedCityName = "Enugu";
                System.out.println("Stored city " + storedCityName);
                String[] city = storedCityName.split(",");
                if(!TextUtils.isEmpty(city[0])){
                    System.out.println("Stored city " + city[0]);
                    String url ="http://api.openweathermap.org/data/2.5/weather?q="+city[0]+"&APPID=62f6de3f7c0803216a3a13bbe4ea9914&units=metric";
                    makeJsonObject(url);
                }
            }
        }
        ImageButton addLocation = (ImageButton) findViewById(R.id.add_location);
        addLocation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent addLocationIntent = new Intent(WeatherActivity.this, AddLocationActivity.class);
                startActivity(addLocationIntent);
            }
        });
        GridLayoutManager gridLayoutManager = new GridLayoutManager(WeatherActivity.this, 4);
        recyclerView = (RecyclerView)findViewById(R.id.weather_daily_list);
        recyclerView.setLayoutManager(gridLayoutManager);
        recyclerView.setHasFixedSize(true);
    }
    private void makeJsonObject(final String apiUrl){
        StringRequest stringRequest = new StringRequest(Request.Method.GET, apiUrl, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d(TAG, "Response " + response);
                GsonBuilder builder = new GsonBuilder();
                Gson gson = builder.create();
                locationMapObject = gson.fromJson(response, LocationMapObject.class);
                if (null == locationMapObject) {
                    Toast.makeText(getApplicationContext(), "Nothing was returned", Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(getApplicationContext(), "Response Good", Toast.LENGTH_LONG).show();
                    String city = locationMapObject.getName() + ", " + locationMapObject.getSys().getCountry();
                    String todayDate = getTodayDateInStringFormat();
                    Long tempVal = Math.round(Math.floor(Double.parseDouble(locationMapObject.getMain().getTemp())));
                    String weatherTemp = String.valueOf(tempVal) + "°";
                    String weatherDescription = Helper.capitalizeFirstLetter(locationMapObject.getWeather().get(0).getDescription());
                    String windSpeed = locationMapObject.getWind().getSpeed();
                    String humidityValue = locationMapObject.getMain().getHumudity();
                    //save location in database
                    if(apiUrl.contains("lat")){
                        query.insertNewLocation(locationMapObject.getName());
                    }
                    // populate View data
                    cityCountry.setText(Html.fromHtml(city));
                    currentDate.setText(Html.fromHtml(todayDate));
                    circleTitle.setTitleText(Html.fromHtml(weatherTemp).toString());
                    circleTitle.setSubtitleText(Html.fromHtml(weatherDescription).toString());
                    windResult.setText(Html.fromHtml(windSpeed) + " km/h");
                    humidityResult.setText(Html.fromHtml(humidityValue) + " %");
                    fiveDaysApiJsonObjectCall(locationMapObject.getName());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d(TAG, "Error " + error.getMessage());
            }
        });
        queue.add(stringRequest);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_LOCATION) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                    //make api call
                    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 100, 2, this);
                    if (locationManager != null) {
                        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        apiUrl = "http://api.openweathermap.org/data/2.5/weather?lat="+location.getLatitude()+"&lon="+location.getLongitude()+"&APPID=62f6de3f7c0803216a3a13bbe4ea9914&units=metric";
                        makeJsonObject(apiUrl);
                    }else{
                        apiUrl = "http://api.openweathermap.org/data/2.5/weather?lat=51.5074&lon=0.1278&APPID=62f6de3f7c0803216a3a13bbe4ea9914&units=metric";
                        makeJsonObject(apiUrl);
                    }
                }
            }else{
                Toast.makeText(WeatherActivity.this, getString(R.string.permission_notice), Toast.LENGTH_LONG).show();
            }
        }
    }
    @Override
    public void onLocationChanged(Location location) {
        this.location = location;
    }
    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {
    }
    @Override
    public void onProviderEnabled(String s) {
    }
    @Override
    public void onProviderDisabled(String provider) {
        if (provider.equals(LocationManager.GPS_PROVIDER)) {
            showGPSDisabledAlertToUser();
        }
    }
    private void showGPSDisabledAlertToUser() {
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
        alertDialogBuilder.setMessage("GPS is disabled in your device. Would you like to enable it?")
                .setCancelable(false)
                .setPositiveButton("Goto Settings Page To Enable GPS", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        Intent callGPSSettingIntent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                        startActivity(callGPSSettingIntent);
                    }
                });
        alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
            }
        });
        AlertDialog alert = alertDialogBuilder.create();
        alert.show();
    }
    private String getTodayDateInStringFormat(){
        Calendar c = Calendar.getInstance();
        SimpleDateFormat df = new SimpleDateFormat("E, d MMMM", Locale.getDefault());
        return df.format(c.getTime());
    }
    private void fiveDaysApiJsonObjectCall(String city){
        String apiUrl = "http://api.openweathermap.org/data/2.5/forecast?q="+city+ "&APPID=62f6de3f7c0803216a3a13bbe4ea9914&units=metric";
        final List<WeatherObject> daysOfTheWeek = new ArrayList<WeatherObject>();
        StringRequest stringRequest = new StringRequest(Request.Method.GET, apiUrl, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d(TAG, "Response 5 days" + response);
                GsonBuilder builder = new GsonBuilder();
                Gson gson = builder.create();
                Forecast forecast = gson.fromJson(response, Forecast.class);
                if (null == forecast) {
                    Toast.makeText(getApplicationContext(), "Nothing was returned", Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(getApplicationContext(), "Response Good", Toast.LENGTH_LONG).show();
                    int[] everyday = new int[]{0,0,0,0,0,0,0};
                    List<FiveWeathers> weatherInfo = forecast.getList();
                    for(int i = 0; i < weatherInfo.size(); i++){
                        String time = weatherInfo.get(i).getDt_txt();
                        String shortDay = convertTimeToDay(time);
                        String temp = weatherInfo.get(i).getMain().getTemp();
                        String tempMin = weatherInfo.get(i).getMain().getTemp_min();
                        if(convertTimeToDay(time).equals("Mon") && everyday[0] < 1){
                            daysOfTheWeek.add(new WeatherObject(shortDay, R.drawable.small_weather_icon, temp, tempMin));
                            everyday[0] = 1;
                        }
                        if(convertTimeToDay(time).equals("Tue") && everyday[1] < 1){
                            daysOfTheWeek.add(new WeatherObject(shortDay, R.drawable.small_weather_icon, temp, tempMin));
                            everyday[1] = 1;
                        }
                        if(convertTimeToDay(time).equals("Wed") && everyday[2] < 1){
                            daysOfTheWeek.add(new WeatherObject(shortDay, R.drawable.small_weather_icon, temp, tempMin));
                            everyday[2] = 1;
                        }
                        if(convertTimeToDay(time).equals("Thu") && everyday[3] < 1){
                            daysOfTheWeek.add(new WeatherObject(shortDay, R.drawable.small_weather_icon, temp, tempMin));
                            everyday[3] = 1;
                        }
                        if(convertTimeToDay(time).equals("Fri") && everyday[4] < 1){
                            daysOfTheWeek.add(new WeatherObject(shortDay, R.drawable.small_weather_icon, temp, tempMin));
                            everyday[4] = 1;
                        }
                        if(convertTimeToDay(time).equals("Sat") && everyday[5] < 1){
                            daysOfTheWeek.add(new WeatherObject(shortDay, R.drawable.small_weather_icon, temp, tempMin));
                            everyday[5] = 1;
                        }
                        if(convertTimeToDay(time).equals("Sun") && everyday[6] < 1){
                            daysOfTheWeek.add(new WeatherObject(shortDay, R.drawable.small_weather_icon, temp, tempMin));
                            everyday[6] = 1;
                        }
                        recyclerViewAdapter = new RecyclerViewAdapter(WeatherActivity.this, daysOfTheWeek);
                        recyclerView.setAdapter(recyclerViewAdapter);
                    }
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d(TAG, "Error " + error.getMessage());
            }
        });
        queue.add(stringRequest);
    }
    private String convertTimeToDay(String time){
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:SSSS", Locale.getDefault());
        String days = "";
        try {
            Date date = format.parse(time);
            System.out.println("Our time " + date);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            days = calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault());
            System.out.println("Our time " + days);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return days;
    }
}

The corresponding layout file for this activity class is shown below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/colorBackground"
    tools:context="com.inducesmile.androidweatherapp.WeatherActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
        android:orientation="vertical">
        <TextView
            android:id="@+id/city_country"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/city_country"
            android:textSize="@dimen/_24sdp"
            android:layout_marginTop="@dimen/_16sdp"
            android:textColor="@color/colorWhite"/>
        <TextView
            android:id="@+id/current_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/date_today"
            android:textSize="@dimen/_14sdp"
            android:layout_marginTop="@dimen/_8sdp"
            android:textColor="@color/colorWhite"/>
        <ImageView
            android:id="@+id/weather_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:contentDescription="@string/app_name"
            android:src="@drawable/sun"
            android:layout_marginTop="@dimen/_24sdp"/>
        
        <com.github.pavlospt.CircleView
            android:id="@+id/weather_result"
            android:layout_width="@dimen/_150sdp"
            android:layout_height="@dimen/_150sdp"
            android:layout_marginTop="@dimen/_8sdp"
            app:cv_titleSubtitleSpace="40"
            app:cv_fillColor="@color/colorBackground"
            app:cv_strokeColorValue="@color/colorCircleStroke"
            app:cv_backgroundColorValue="@color/colorCircleStroke"
            app:cv_titleColor="@color/colorWhite"
            app:cv_titleSize="@dimen/_50sdp"
            app:cv_titleText="@string/current_temperature"
            app:cv_subtitleSize="@dimen/_12sdp"
            app:cv_subtitleText="@string/weather_information"
            android:layout_gravity="center_horizontal"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:weightSum="3"
            android:layout_marginTop="@dimen/_12sdp"
            android:layout_gravity="center_horizontal">
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:paddingLeft="@dimen/_16sdp"
                android:layout_weight="1">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/colorSubTitle"
                    android:textSize="@dimen/_14sdp"
                    android:textStyle="bold"
                    android:text="@string/wind"/>
                <TextView
                    android:id="@+id/wind_result"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/colorWhite"
                    android:textSize="@dimen/_14sdp"
                    android:layout_marginTop="@dimen/_4sdp"
                    android:text="@string/wind_speed"/>
            </LinearLayout>
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:layout_weight="1">
                <ImageButton
                    android:id="@+id/add_location"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:background="@android:color/transparent"
                    android:elevation="@dimen/_4sdp"
                    android:layout_marginTop="@dimen/_12sdp"
                    android:src="@drawable/cross"/>
            </LinearLayout>
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:paddingRight="@dimen/_16sdp"
                android:layout_weight="1">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/colorSubTitle"
                    android:textSize="@dimen/_14sdp"
                    android:layout_gravity="right"
                    android:textStyle="bold"
                    android:text="@string/humidity"/>
                <TextView
                    android:id="@+id/humidity_result"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/colorWhite"
                    android:layout_gravity="center_horizontal"
                    android:textSize="@dimen/_14sdp"
                    android:layout_marginTop="@dimen/_4sdp"
                    android:text="@string/humidity_rate"/>
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:background="@color/colorBottomBackground"
        android:orientation="vertical">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/weather_daily_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center_horizontal"
            android:orientation="vertical"
            android:scrollbars="none"/>
    </LinearLayout>
</LinearLayout>

Adapter Layout File

The adapter classes that we created inflate some layout file. Create below layout file in the layout folder and add the code to each file.

city_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/text_suggestion"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/colorBlack"
        android:padding="@dimen/_16sdp"
        android:text="@string/app_name"/>
</LinearLayout>

location_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/_16sdp"
    android:layout_marginLeft="@dimen/_16sdp"
    android:layout_marginRight="@dimen/_16sdp"
    android:padding="@dimen/_16sdp"
    android:background="@color/colorPrimary"
    android:orientation="horizontal">
    <LinearLayout
        android:id="@+id/radio_button_wrapper"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical">
        <RadioButton
            android:id="@+id/radio_button"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/location_wrapper"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="8"
        android:layout_marginLeft="@dimen/_8sdp"
        android:orientation="vertical">
        <TextView
            android:id="@+id/city_location"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_14sdp"
            android:text="@string/city_country"
            android:textColor="@color/colorWhite"/>
        <TextView
            android:id="@+id/temp_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="@dimen/_12sdp"
            android:text="@string/temp_information"
            android:layout_marginTop="@dimen/_4sdp"
            android:textColor="@color/colorWhite"/>
    </LinearLayout>
    <LinearLayout
        android:id="@+id/delete_button_wrapper"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical">
        <TextView
            android:id="@+id/delete_row"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="@dimen/_22sdp"
            android:text="@string/delete_text"
            android:gravity="center"
            android:layout_gravity="center"
            android:textColor="@color/colorWhite"/>
    </LinearLayout>
</LinearLayout>

weather_daily_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="@dimen/_16sdp"
    android:paddingRight="@dimen/_16sdp"
    android:paddingBottom="@dimen/_4sdp"
    android:paddingTop="@dimen/_4sdp"
    android:orientation="vertical">
    <TextView
        android:id="@+id/day_of_week"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="@dimen/_12sdp"
        android:text="@string/day_of_week"
        android:textColor="@color/colorWhite"/>
    <ImageView
        android:id="@+id/weather_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/app_name"
        android:src="@drawable/small_weather_icon"
        android:layout_marginTop="@dimen/_4sdp"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android:id="@+id/weather_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="@dimen/_4sdp"
        android:textSize="@dimen/_11sdp"
        android:text="@string/current_temperature"
        android:textColor="@color/colorWhite"/>
    <TextView
        android:id="@+id/weather_result_small"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="@dimen/_4sdp"
        android:textSize="@dimen/_8sdp"
        android:text="@string/current_temperature"
        android:textColor="@color/colorWhite"/>
</LinearLayout>

This brings us to the end of this tutorial. I hope that you have learn something. Run your app and see for yourself.

You can download the code for this tutorial below. If you are having hard time downloading the tutorial, kindly contact me.

Remember to subscribe with your email address so that you will be among the first to receive my new android blog post once it is published.

Please if you love this tutorial, kindly download my android app – Complete Mathematics – in Google Play Store and let me know what you think about it.

OTHER INTERESTING POSTS:

14 Comments

    • Henry
    • Henry
    • Henry

Add a Comment