package de.apps4ics.mountainnavigation.handlers;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.content.res.Resources;
import android.location.Location;
import android.util.Log;
import com.survivingwithandroid.weather.lib.WeatherClient;
import com.survivingwithandroid.weather.lib.WeatherConfig;
import com.survivingwithandroid.weather.lib.exception.WeatherLibException;
import com.survivingwithandroid.weather.lib.exception.WeatherProviderInstantiationException;
import com.survivingwithandroid.weather.lib.model.CurrentWeather;
import com.survivingwithandroid.weather.lib.model.DayForecast;
import com.survivingwithandroid.weather.lib.model.HistoricalHourWeather;
import com.survivingwithandroid.weather.lib.model.HistoricalWeather;
import com.survivingwithandroid.weather.lib.model.Weather;
import com.survivingwithandroid.weather.lib.model.WeatherForecast;
import com.survivingwithandroid.weather.lib.provider.openweathermap.OpenweathermapProviderType;
import com.survivingwithandroid.weather.lib.request.WeatherRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import de.apps4ics.mountainnavigation.InformDialog;
import de.apps4ics.mountainnavigation.InformListDialog;
import de.apps4ics.mountainnavigation.MainActivity;
import de.apps4ics.mountainnavigation.MyWeather;
import de.apps4ics.mountainnavigation.OnSingleWeatherRetrieved;
import de.apps4ics.mountainnavigation.OnWeatherRetrieved;
import de.apps4ics.mountainnavigation.R;
import de.apps4ics.mountainnavigation.SettingsActivity;
import de.apps4ics.mountainnavigation.WeatherDatabase;
import de.apps4ics.mountainnavigation.adapters.WarningListAdapter;
/**
* This file is part of MountainNavigation.
*
* MountainNavigation is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MountainNavigation is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MountainNavigation. If not, see <http://www.gnu.org/licenses/>.
*
* @copyright Copyright (c) 2016 Vinzenz Rosenkanz <vinzenz.rosenkranz@gmail.com>
*
* @author Vinzenz Rosenkranz <vinzenz.rosenkranz@gmail.com>
*/
public class WeatherHandler {
private static final String API_KEY = "fd4034defae557fd5f2fdaaf73c3402c"; //OpenWeatherMap API Key
private Context context;
private WeatherDatabase weatherDb;
private WeatherClient weatherClient;
private WeatherConfig weatherConfig;
private HistoricalWeather histWeather;
private DayForecast[] forecasts;
private Weather[] weathers;
public final static int MIN_HOT_TEMP_DEFAULT = 30;
public final static int MAX_COLD_TEMP_DEFAULT = 10;
public final static int MAX_DAYS_FORECAST_DEFAULT = 3;
public final static int MIN_DAYS_FORECAST = 1;
private static int MIN_HOT_TEMP;
private static int MAX_COLD_TEMP;
private static int MAX_DAYS_FORECAST;
private static float MIN_WINDY_SPEED;
private static Activity activity;
private static FragmentManager fragmentManager;
public WeatherHandler(Context context) {
weatherDb = new WeatherDatabase(context);
this.context = context;
try {
activity = (Activity) context;
fragmentManager = activity.getFragmentManager();
} catch(ClassCastException e) {
Log.e(MainActivity.TAG, "Could not get fragment manager!");
//TODO close app?
}
Resources res = MainActivity.getRes();
MAX_DAYS_FORECAST = SettingsActivity.getDaysForecastFromPrefs(MAX_DAYS_FORECAST_DEFAULT);
WeatherClient.ClientBuilder weatherBuilder = new WeatherClient.ClientBuilder();
weatherConfig = new WeatherConfig();
weatherConfig.unitSystem = WeatherConfig.UNIT_SYSTEM.M;
weatherConfig.ApiKey = API_KEY;
weatherConfig.lang = res.getString(R.string.lang_identifier);
weatherConfig.numDays = MAX_DAYS_FORECAST;
MIN_HOT_TEMP = SettingsActivity.getMinHotFromPrefs(MIN_HOT_TEMP_DEFAULT);
MAX_COLD_TEMP = SettingsActivity.getMaxColdFromPrefs(MAX_COLD_TEMP_DEFAULT);
MIN_WINDY_SPEED = 8; //http://www.wettergefahren-fruehwarnung.de/Artikel/beaufort.html
if(weatherConfig.unitSystem.equals(WeatherConfig.UNIT_SYSTEM.I)){
MIN_HOT_TEMP = (int) celsiusToFahrenheit(MIN_HOT_TEMP);
MAX_COLD_TEMP = (int) celsiusToFahrenheit(MAX_COLD_TEMP);
}
weatherClient = null;
try {
weatherClient = weatherBuilder.attach(context)
.provider(new OpenweathermapProviderType())
.httpClient(com.survivingwithandroid.weather.lib.client.okhttp.WeatherDefaultClient.class)
.config(weatherConfig)
.build();
} catch (WeatherProviderInstantiationException e) {
e.printStackTrace();
}
}
public void getCurrentWeather(OnSingleWeatherRetrieved callback) {
getCurrentWeather(MainActivity.getLocation(), callback);
}
public void getCurrentWeather(Location l, OnSingleWeatherRetrieved callback) {
getCurrentWeather(l.getLatitude(), l.getLongitude(), callback);
}
public void getCurrentWeather(double lat, double lon, OnSingleWeatherRetrieved callback) {
if(MainActivity.hasInternet()) getCurrentWeatherOnline(lat, lon, callback);
else getCurrentWeatherCached(lat, lon, 0, callback);
}
//TODO
private void getCurrentWeatherOnline(double lat, double lon, final OnSingleWeatherRetrieved callback) {
weatherClient.getCurrentCondition(new WeatherRequest(lon, lat), new WeatherClient.WeatherEventListener() {
@Override
public void onWeatherRetrieved(CurrentWeather weather) {
MyWeather myWeather = new MyWeather(weather.weather, System.currentTimeMillis());
callback.postRetrieve(myWeather);
}
@Override
public void onWeatherError(WeatherLibException wle) {
}
@Override
public void onConnectionError(Throwable t) {
}
});
}
//TODO
private void getCurrentWeatherCached(double lat, double lon, int offset, OnSingleWeatherRetrieved callback) {
getFromDatabase(lat, lon, offset, callback);
}
public void getForecast(int days, OnWeatherRetrieved callback) {
getForecast(MainActivity.getLocation(), days, callback);
}
public void getForecast(Location l, OnWeatherRetrieved callback) {
if(l == null) return;
getForecast(l.getLatitude(), l.getLongitude(), callback);
}
public void getForecast(Location l, int days, OnWeatherRetrieved callback) {
if(l == null) return;
getForecast(l.getLatitude(), l.getLongitude(), days, callback);
}
public void getForecast(double lat, double lon, OnWeatherRetrieved callback) {
getForecast(lat, lon, MAX_DAYS_FORECAST, callback);
}
public void getForecast(double lat, double lon, int days, OnWeatherRetrieved callback) {
if(MainActivity.hasInternet()) getForecastOnline(lat, lon, days, callback);
else getForecastCached(lat, lon, days, 0, callback);
}
public void getForecastOnline(double lat, double lon, final int days, final OnWeatherRetrieved callback) {
weatherConfig.numDays = days;
weatherClient.updateWeatherConfig(weatherConfig);
weatherClient.getForecastWeather(new WeatherRequest(lon, lat), new WeatherClient.ForecastWeatherEventListener() {
@Override
public void onWeatherRetrieved(WeatherForecast forecast) {
List<MyWeather> weatherList = new ArrayList<>();
List<DayForecast> foundForecasts = forecast.getForecast();
int dayCount = Math.min(foundForecasts.size(), days);
if(dayCount == 0) return;
forecasts = new DayForecast[dayCount];
weathers = new Weather[dayCount];
for(int i=0; i<dayCount; i++) {
DayForecast curr = foundForecasts.get(i);
forecasts[i] = curr;
weathers[i] = curr.weather;
MyWeather newWeather = new MyWeather(curr);
insertIntoDatabase(newWeather);
weatherList.add(newWeather);
}
Log.d(MainActivity.TAG, "City [" + weathers[0].location.getCity() + "] Current temp: " + weathers[0].temperature.getTemp());
String title = String.format("Weather for %s in %s", weathers[0].location.getCity(), weathers[0].location.getCountry());
String msg = String.format("%1$.1f%2$s (%3$.1f%4$s/%5$.1f%6$s)\nSunrise: %7$s\nSunset: %8$s\nWeatherCode: %9$d\nWeatherId: %10$d",
forecasts[0].forecastTemp.day,
forecast.getUnit().tempUnit,
forecasts[0].forecastTemp.min,
forecast.getUnit().tempUnit,
forecasts[0].forecastTemp.max,
forecast.getUnit().tempUnit,
MainActivity.df_hm.format(new Date(weathers[0].location.getSunrise() * 1000)),
MainActivity.df_hm.format(new Date(weathers[0].location.getSunset() * 1000)),
weathers[0].currentCondition.getWeatherCode().getCode(),
weathers[0].currentCondition.getWeatherId());
Log.d(MainActivity.TAG, title + ": " + msg);
callback.postRetrieve(weatherList);
//displayHints(weatherList.get(0));
//displaySevereWeather(weatherList.get(0));
}
@Override
public void onWeatherError(WeatherLibException wle) {
Log.e(MainActivity.TAG, "Weather error - parsing data");
wle.printStackTrace();
}
@Override
public void onConnectionError(Throwable t) {
Log.e(MainActivity.TAG, "Connection Error");
t.printStackTrace();
}
});
}
//TODO
public void getForecastCached(double lat, double lon, int days, int offset, OnWeatherRetrieved callback) {
return;
}
public void getHistoricalWeather(long start, long end, OnWeatherRetrieved callback) {
getHistoricalWeather(MainActivity.getLocation(), start, end, callback);
}
public void getHistoricalWeather(Location l, long start, long end, OnWeatherRetrieved callback) {
if(l == null) return;
getHistoricalWeather(l.getLatitude(), l.getLongitude(), start, end, callback);
}
public void getHistoricalWeather(double lat, double lon, long start, long end, OnWeatherRetrieved callback){
if(MainActivity.hasInternet()) getHistoricalWeatherOnline(lat, lon, start, end, callback);
else getHistoricalWeatherCached(lat, lon, start, end, callback);
}
public void getHistoricalWeatherOnline(double lat, double lon, long start, long end, OnWeatherRetrieved callback) {
ArrayList<MyWeather> weathers = new ArrayList<>();
Date startDate = new Date(start / 1000); //OWM uses seconds instead of milliseconds
Date endDate = new Date(end / 1000);
histWeather = null;
weatherClient.getHistoricalWeather(new WeatherRequest(lon, lat), startDate, endDate, new WeatherClient.HistoricalWeatherEventListener() {
@Override
public void onWeatherRetrieved(HistoricalWeather historicalWeather) {
//TODO check if it was sunny/rainy and set fountain options based on this
histWeather = historicalWeather;
}
@Override
public void onWeatherError(WeatherLibException wle) {
Log.e(MainActivity.TAG, "Weather error - parsing data");
wle.printStackTrace();
}
@Override
public void onConnectionError(Throwable t) {
Log.e(MainActivity.TAG, "Connection Error");
t.printStackTrace();
}
});
for(HistoricalHourWeather hhw : histWeather.getHoistoricalData()) {
MyWeather curr = new MyWeather(hhw.weather, hhw.timestamp);
insertIntoDatabase(curr);
weathers.add(curr);
}
callback.postRetrieve(weathers);
}
private void getHistoricalWeatherCached(double lat, double lon, long start, long end, OnWeatherRetrieved callback) {
return;
}
private void insertIntoDatabase(Weather weather) {
insertIntoDatabase(weather, System.currentTimeMillis());
}
private void insertIntoDatabase(Weather weather, long timestamp) {
insertIntoDatabase(new MyWeather(weather, timestamp));
}
private void insertIntoDatabase(MyWeather weather) {
weatherDb.insertWeather(weather);
}
private void getFromDatabase(double lat, double lon, int offset, OnSingleWeatherRetrieved callback) {
MyWeather weather = weatherDb.getWeather(lat, lon, offset);
callback.postRetrieve(weather);
}
public void displayHints(MyWeather weather) {
//TODO add more hints
if (MainActivity.showWeatherHints()) {
Resources res = MainActivity.getRes();
List<String> titles = new ArrayList<>();
List<String> descs = new ArrayList<>();
List<String> infos = new ArrayList<>();
if (weather.isSunny()) {
titles.add(res.getString(R.string.hint_dialog_titles_sunny));
descs.add(res.getString(R.string.hint_dialog_msgs_sunny));
infos.add("");
}
if (weather.isHot()) {
titles.add(res.getString(R.string.hint_dialog_titles_hot));
descs.add(res.getString(R.string.hint_dialog_msgs_hot));
infos.add(res.getString(R.string.hint_dialog_info_hot));
}
if (weather.isCold()) {
titles.add(res.getString(R.string.hint_dialog_titles_cold));
descs.add(res.getString(R.string.hint_dialog_msgs_cold));
infos.add("");
}
if (weather.isWindy()) {
titles.add(res.getString(R.string.hint_dialog_titles_windy));
descs.add(res.getString(R.string.hint_dialog_msgs_windy));
infos.add("");
}
if (weather.isAfterSunset()) {
titles.add(res.getString(R.string.hint_dialog_titles_sunset));
descs.add(res.getString(R.string.hint_dialog_msgs_sunset));
infos.add("");
}
if (weather.isFoggy()) {
titles.add(res.getString(R.string.hint_dialog_titles_foggy));
descs.add(res.getString(R.string.hint_dialog_msgs_foggy));
infos.add(res.getString(R.string.hint_dialog_info_foggy));
}
if (weather.hasSleet()) {
titles.add(res.getString(R.string.hint_dialog_titles_sleet));
descs.add(res.getString(R.string.hint_dialog_msgs_sleet));
infos.add("");
}
if (titles.size() > 0 && descs.size() > 0) {
String[] titleArr = new String[titles.size()];
String[] descArr = new String[descs.size()];
String[] infosArr = new String[infos.size()];
titleArr = titles.toArray(titleArr);
descArr = descs.toArray(descArr);
infosArr = infos.toArray(infosArr);
WarningListAdapter warningAdapter = new WarningListAdapter(activity, titleArr, descArr, infosArr);
InformListDialog weatherHintDialog = new InformListDialog(MainActivity.getRes().getString(R.string.hint_dialog_title), warningAdapter);
weatherHintDialog.show(fragmentManager, "Warning Hint Dialog");
}
}
}
public void displaySevereWeather(MyWeather weather) {
if (weather.isSevereWeather(weather.getWeatherCode())) {
Resources res = context.getResources();
String warnMsg = String.format(res.getString(R.string.weather_warning_dialog_msg), weather.getCity(), weather.getWeatherCode());
InformDialog informAlertDialog = new InformDialog(res.getString(R.string.weather_warning_dialog_title), warnMsg);
informAlertDialog.show(fragmentManager, "Inform Dialog - Weather Warning");
}
}
public static float getMinHotTemp() {
return MIN_HOT_TEMP;
}
public static float getMinWindySpeed() {
return MIN_WINDY_SPEED;
}
public static int getMaxDaysForecast() {
return MAX_DAYS_FORECAST;
}
public static float getMaxColdTemp() {
return MAX_COLD_TEMP;
}
public static float celsiusToFahrenheit(float c) {
return c * 9.0f/5.0f + 32;
}
public static float fahrenheitToCelsius(float f) {
return (f - 32.0f) * 5.0f/9.0f;
}
}