Alvaro2112/Money_run

View on GitHub
app/src/main/java/sdp/moneyrun/weather/OpenWeatherMap.java

Summary

Maintainability
A
1 hr
Test Coverage
B
84%
package sdp.moneyrun.weather;


import android.os.StrictMode;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

import javax.net.ssl.HttpsURLConnection;

import sdp.moneyrun.location.LocationRepresentation;


public class OpenWeatherMap {
    public static final String open_weather_key = "052c4316c52f5d7f619a05a0a09a7636";
    private static final String API_ENDPOINT = "https://api.openweathermap.org/data/2.5/onecall";
    private static final String TEMP_UNIT = "metric";
    private static final WeatherReport NO_DATA = new WeatherReport(0, 0, 0, "N/A", "N/A");
    private final String apiKey;

    OpenWeatherMap(String apiKey) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());

        this.apiKey = apiKey;
    }

    @NonNull
    public static OpenWeatherMap build() {
        return new OpenWeatherMap(open_weather_key);
    }

    /**
     * This function will get a report as a JSONObject and will parse it to convert it into a usable WeatherReport Object
     *
     * @param report The report to be converted
     * @return The converted report
     */
    @NonNull
    private WeatherReport parseReport(@Nullable JSONObject report) throws JSONException {
        if (report == null) {
            throw new NullPointerException();
        }

        JSONObject weather = report.getJSONArray("weather").getJSONObject(0);

        return new WeatherReport(
                report.getJSONObject("temp").getDouble("day"),
                report.getJSONObject("temp").getDouble("min"),
                report.getJSONObject("temp").getDouble("max"),

                weather.getString("main"),
                weather.getString("icon")
        );
    }

    /**
     * This function will get a forecast as a JSONObject and will parse it to convert it into a usable WeatherForecast Object
     *
     * @param forecast The weatherForecast to be converted
     * @return The converted weatherForecast
     */
    @NonNull
    private WeatherForecast parseForecast(@Nullable JSONObject forecast) throws JSONException {
        if (forecast == null)
            throw new NullPointerException();

        JSONArray daily = forecast.getJSONArray("daily");
        WeatherReport report;

        if (0 == daily.length())
            report = NO_DATA;
        else
            report = tryToParseReport(daily.getJSONObject(0));

        return new WeatherForecast(report);
    }

    /**
     * Basically the parseForecast function surrounded by a try catch in case an Exception occurs
     *
     * @param jsonObject The Report to be converted
     * @return
     */
    private WeatherReport tryToParseReport(JSONObject jsonObject) {

        WeatherReport report;

        try {
            report = parseReport(jsonObject);
        } catch (JSONException ex) {
            Log.e("OpenWeatherMapWeather", "Error when parsing day 0", ex);
            report = NO_DATA;
        }

        return report;
    }

    /**
     * Gets the forecast as a string for a given location from the OpenWeather website
     *
     * @param location The location for which we want the weather forecast
     * @return The weather forecast as a String
     */
    @Nullable
    private String getRawForecast(@NonNull LocationRepresentation location) throws IOException {
        String queryUrl = API_ENDPOINT +
                "?lat=" + location.getLatitude() +
                "&lon=" + location.getLongitude() +
                "&units=" + TEMP_UNIT +
                "&exclude=current,minutely,hourly" +
                "&appid=" + apiKey;

        URL url = new URL(queryUrl);

        InputStream stream = null;
        HttpsURLConnection connection = null;
        String result = null;
        try {
            connection = (HttpsURLConnection) url.openConnection();
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");

            connection.setDoInput(true);
            connection.connect();

            int responseCode = connection.getResponseCode();
            if (responseCode != HttpsURLConnection.HTTP_OK) {
                throw new IOException("HTTP error code: " + responseCode);
            }

            stream = connection.getInputStream();
            if (stream != null) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
                result = reader.lines().collect(Collectors.joining("\n"));
            }
        } finally {
            if (stream != null) {
                stream.close();
            }
            if (connection != null) {
                connection.disconnect();
            }
        }
        return result;

    }

    /**
     * Returns the weatherForecast for a given location
     *
     * @param location the location for which we want the forecast
     * @return the forecast
     */
    @NonNull
    public WeatherForecast getForecast(@NonNull LocationRepresentation location) throws IOException {
        String forecast = getRawForecast(location);
        try {
            JSONObject json = (JSONObject) new JSONTokener(forecast).nextValue();
            return parseForecast(json);
        } catch (JSONException e) {
            throw new IOException(e);
        }
    }
}