/*
* Copyright (C) 2014 Francesco Azzola
* Surviving with Android (http://www.survivingwithandroid.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.survivingwithandroid.weather.lib.client.okhttp;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Criteria;
import android.location.Location;
import android.os.Handler;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.survivingwithandroid.weather.lib.WeatherClient;
import com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException;
import com.survivingwithandroid.weather.lib.exception.LocationProviderNotFoundException;
import com.survivingwithandroid.weather.lib.exception.WeatherLibException;
import com.survivingwithandroid.weather.lib.model.City;
import com.survivingwithandroid.weather.lib.model.CurrentWeather;
import com.survivingwithandroid.weather.lib.model.HistoricalWeather;
import com.survivingwithandroid.weather.lib.model.HourForecast;
import com.survivingwithandroid.weather.lib.model.WeatherForecast;
import com.survivingwithandroid.weather.lib.model.WeatherHourForecast;
import com.survivingwithandroid.weather.lib.request.Params;
import com.survivingwithandroid.weather.lib.request.WeatherProviderFeature;
import com.survivingwithandroid.weather.lib.request.WeatherRequest;
import com.survivingwithandroid.weather.lib.response.GenericResponseParser;
import com.survivingwithandroid.weather.lib.util.LogUtils;
import java.io.IOException;
import java.util.Date;
import java.util.List;
public class WeatherDefaultClient extends WeatherClient {
/**
* Get the current weather condition. It returns a class structure that is indipendent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.WeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the current weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param location a String representing the location id
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.WeatherEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
* @deprecated use instead {@link com.survivingwithandroid.weather.lib.WeatherClient#getCurrentCondition(com.survivingwithandroid.weather.lib.request.WeatherRequest, com.survivingwithandroid.weather.lib.WeatherClient.WeatherEventListener)}
*/
@Override
public void getCurrentCondition(String location, final WeatherEventListener listener) throws ApiKeyRequiredException {
getCurrentCondition(new WeatherRequest(location), listener);
}
/**
* Search the city using a name pattern. It returns a class structure that is indipendent from the
* provider used that holds the city list matching the pattern.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.CityEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onCityListRetrieved passing a {@link java.util.List} of cities.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param pattern a String representing the pattern
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.CityEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
*/
@Override
public void searchCity(String pattern, final CityEventListener listener) throws ApiKeyRequiredException {
String url = provider.getQueryCityURL(pattern);
_doSearchCity(url, listener);
/*
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final List<City> result = provider.getCityResultList(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onCityListRetrieved(result);
}
});
} catch (WeatherLibException e) {
//listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
*/
}
/**
* Search the city using latitude and longitude. It returns a class structure that is indipendent from the
* provider used that holds the city list matching the pattern.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.CityEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onCityListRetrieved passing a {@link java.util.List} of cities.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param lat a double representing the latitude
* @param lon a double representing the longitude
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.CityEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
* @since 1.5.3
*/
@Override
public void searchCity(double lat, double lon, CityEventListener listener) throws ApiKeyRequiredException {
String url = provider.getQueryCityURLByCoord(lon, lat);
_doSearchCity(url, listener);
}
private void _doSearchCity(String url, final CityEventListener listener) throws ApiKeyRequiredException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final List<City> result = provider.getCityResultList(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onCityListRetrieved(result);
}
});
} catch (WeatherLibException e) {
//listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
}
/**
* Get the forecast weather condition. It returns a class structure that is independent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.ForecastWeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the {@link com.survivingwithandroid.weather.lib.model.WeatherForecast} weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param location a String representing the location id
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.ForecastWeatherEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
* @deprecated use instead {@link com.survivingwithandroid.weather.lib.WeatherClient#getForecastWeather(com.survivingwithandroid.weather.lib.request.WeatherRequest, com.survivingwithandroid.weather.lib.WeatherClient.ForecastWeatherEventListener)}
*/
@Override
public void getForecastWeather(String location, final ForecastWeatherEventListener listener) throws ApiKeyRequiredException {
getForecastWeather(new WeatherRequest(location), listener);
}
/**
* Get the forecast weather condition. It returns a class structure that is independent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HourForecastWeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the {@link com.survivingwithandroid.weather.lib.model.WeatherHourForecast} weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param location a String representing the location id
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HourForecastWeatherEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
* @deprecated use instead {@link com.survivingwithandroid.weather.lib.WeatherClient#getHourForecastWeather(com.survivingwithandroid.weather.lib.request.WeatherRequest, com.survivingwithandroid.weather.lib.WeatherClient.HourForecastWeatherEventListener)}
*/
@Override
public void getHourForecastWeather(String location, final HourForecastWeatherEventListener listener) throws ApiKeyRequiredException {
getHourForecastWeather(new WeatherRequest(location), listener);
}
/**
* Get the historical weather condition. It returns a class structure that is independent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HistoricalWeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the {@link com.survivingwithandroid.weather.lib.model.HistoricalWeather} weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param location a String representing the location id
* @param d1 is the starting date
* @param d2
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HistoricalWeatherEventListener} @param2 d2 is the end date
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
* @deprecated use instead {@link com.survivingwithandroid.weather.lib.WeatherClient#getHistoricalWeather(com.survivingwithandroid.weather.lib.request.WeatherRequest, java.util.Date, java.util.Date, com.survivingwithandroid.weather.lib.WeatherClient.HistoricalWeatherEventListener)}
*/
@Override
public void getHistoricalWeather(String location, Date d1, Date d2, final HistoricalWeatherEventListener listener) {
getHistoricalWeather(new WeatherRequest(location), d1, d2, listener);
}
/**
* This is the default image Provider that can be used to get the image provided by the Weather provider
*
* @param icon String The icon containing the weather code to retrieve the image
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.WeatherImageListener}
*/
@Override
public void getDefaultProviderImage(String icon, WeatherImageListener listener) {
String imageURL = provider.getQueryImageURL(icon);
downloadImage(imageURL, listener);
}
/**
* Search the city using geographic coordinates. It returns a class structure that is independent from the
* provider used that holds the city list matching the pattern.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.CityEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onCityListRetrieved passing a {@link java.util.List} of cities.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param criteria {@link android.location.Criteria}
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.CityEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
*/
@Override
public void searchCityByLocation(Criteria criteria, CityEventListener listener) throws LocationProviderNotFoundException {
super.handleLocation(criteria, listener);
}
/**
* This method retrieves the image using the url generated by the weahter provider {@link com.survivingwithandroid.weather.lib.provider.IWeatherProvider}
*
* @param cityId String representing the city id
* @param params {@link com.survivingwithandroid.weather.lib.request.Params}: list of parameters used to create the image
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.WeatherImageListener} listener that gets notified when the image is ready to use
*/
@Override
public void getWeatherImage(String cityId, Params params, final WeatherImageListener listener) {
String imageURL = provider.getQueryLayerURL(cityId, params);
downloadImage(imageURL, listener);
}
private void downloadImage(String urlImage, final WeatherImageListener listener) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(urlImage).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
final Bitmap bmp = BitmapFactory.decodeStream(response.body().byteStream());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onImageReady(bmp);
}
});
}
});
}
@Override
protected void searchCityByLocation(Location location, final CityEventListener listener) throws ApiKeyRequiredException {
String url = provider.getQueryCityURLByLocation(location);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final List<City> cityList = provider.getCityResultList(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onCityListRetrieved(cityList);
}
});
} catch (WeatherLibException e) {
//listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
}
// New methods
/**
* Get the current weather condition. It returns a class structure that is indipendent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.WeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the current weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param wRequest {@link com.survivingwithandroid.weather.lib.request.WeatherRequest}
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.WeatherEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
*/
@Override
public void getCurrentCondition(WeatherRequest wRequest, final WeatherEventListener listener) throws ApiKeyRequiredException {
String url = provider.getQueryCurrentWeatherURL(wRequest);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final CurrentWeather currentWeather = provider.getCurrentCondition(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onWeatherRetrieved(currentWeather);
}
});
} catch (WeatherLibException e) {
//listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
}
/**
* Get the forecast weather condition. It returns a class structure that is independent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.ForecastWeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the {@link com.survivingwithandroid.weather.lib.model.WeatherForecast} weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param wRequest {@link com.survivingwithandroid.weather.lib.request.WeatherRequest}
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.ForecastWeatherEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
*/
@Override
public void getForecastWeather(WeatherRequest wRequest, final ForecastWeatherEventListener listener) throws ApiKeyRequiredException {
String url = provider.getQueryForecastWeatherURL(wRequest);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final WeatherForecast forecast = provider.getForecastWeather(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onWeatherRetrieved(forecast);
}
});
} catch (WeatherLibException e) {
//listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
}
/**
* Get the forecast weather condition. It returns a class structure that is independent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HourForecastWeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the {@link com.survivingwithandroid.weather.lib.model.WeatherHourForecast} weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param wRequest {@link com.survivingwithandroid.weather.lib.request.WeatherRequest}
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HourForecastWeatherEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
*/
@Override
public void getHourForecastWeather(WeatherRequest wRequest, final HourForecastWeatherEventListener listener) throws ApiKeyRequiredException {
String url = provider.getQueryHourForecastWeatherURL(wRequest);
OkHttpClient client = new OkHttpClient();
if (url == null) {
url = provider.getQueryForecastWeatherURL(wRequest);
}
if (url != null) {
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final WeatherHourForecast forecast = provider.getHourForecastWeather(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onWeatherRetrieved(forecast);
}
});
//listener.onWeatherRetrieved(forecast); // Issue OkHttpClient #7
} catch (WeatherLibException e) {
// listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
}
}
/**
* Get the historical weather condition. It returns a class structure that is independent from the
* provider used to ge the weather data.
* This method is an async method, in other word you have to implement your listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HistoricalWeatherEventListener} to
* get notified when the weather data is ready.
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the {@link com.survivingwithandroid.weather.lib.model.HistoricalWeather} weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param wRequest {@link com.survivingwithandroid.weather.lib.request.WeatherRequest}
* @param d1 is the starting date
* @param d2
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.HistoricalWeatherEventListener} @param2 d2 is the end date
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
*/
@Override
public void getHistoricalWeather(WeatherRequest wRequest, Date d1, Date d2, final HistoricalWeatherEventListener listener) {
String url = provider.getQueryHistoricalWeatherURL(wRequest, d1, d2);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final HistoricalWeather histWeather = provider.getHistoricalWeather(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onWeatherRetrieved(histWeather);
}
});
} catch (WeatherLibException e) {
//listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
}
/**
* Get a specific weather provider feature not implemented in all weather provider
* <p>
* When the data is ready this method calls the onWeatherRetrieved passing the {@link com.survivingwithandroid.weather.lib.model.HistoricalWeather} weather information.
* If there are some errors during the request parsing, it calls onWeatherError passing the exception or
* onConnectionError if the errors happened during the HTTP connection
* </p>
*
* @param wRequest {@link com.survivingwithandroid.weather.lib.request.WeatherRequest}
* @param extRequest is the extended request as required by the weather provider
* @param parser is the parser used to parsed the response {@link com.survivingwithandroid.weather.lib.response.GenericResponseParser}
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.GenericRequestWeatherEventListener}
* @throws com.survivingwithandroid.weather.lib.exception.ApiKeyRequiredException
*/
@Override
public <T extends WeatherProviderFeature, S extends Object> void getProviderWeatherFeature(WeatherRequest wRequest, T extRequest, final GenericResponseParser<S> parser, final GenericRequestWeatherEventListener<S> listener) {
String url = extRequest.getURL();
LogUtils.LOGD("Generic Weather feature URL [" + url + "]");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
notifyConnectionError(e, listener);
}
@Override
public void onResponse(Response response) throws IOException {
try {
final S result = parser.parseData(response.body().string());
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onResponseRetrieved(result);
}
});
} catch (WeatherLibException e) {
//listener.onWeatherError(e);
notifyWeatherError(e, listener);
}
}
});
}
/**
* Get an image at the specified URL and inform the listener when the image is ready
*
* @param url String representing the url
* @param listener {@link com.survivingwithandroid.weather.lib.WeatherClient.WeatherImageListener}
* @since 1.5.3
*/
@Override
public void getImage(String url, WeatherImageListener listener) {
downloadImage(url, listener);
}
// Notify connection error on main thread so that the client can show dialogs,toast etc. to notify the error to the final user
private void notifyConnectionError(final Throwable t, final WeatherClientListener listener) {
Handler handler = new Handler(ctx.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
listener.onConnectionError(t);
}
});
}
private void notifyWeatherError(final WeatherLibException wle, final WeatherClientListener listener) {
Handler handler = new Handler(ctx.getMainLooper()); // Hanlder
handler.post(new Runnable() {
@Override
public void run() {
listener.onWeatherError(wle);
}
});
}
}