package fr.tvbarthel.apps.simplethermometer.services;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import fr.tvbarthel.apps.simplethermometer.R;
import fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParser;
import fr.tvbarthel.apps.simplethermometer.openweathermap.OpenWeatherMapParserResult;
import fr.tvbarthel.apps.simplethermometer.utils.ConnectivityUtils;
import fr.tvbarthel.apps.simplethermometer.utils.LocationUtils;
import fr.tvbarthel.apps.simplethermometer.utils.PreferenceUtils;
import fr.tvbarthel.apps.simplethermometer.widget.STWidgetProvider;
/**
* A simple {@link android.app.IntentService} that updates the temperature.
*/
public class TemperatureUpdaterService extends Service implements LocationListener {
// update action
public static final String ACTION_UPDATE = "TemperatureUpdaterService.Actions.Update";
// update error action
public static final String ACTION_UPDATE_ERROR = "TemperatureUpdaterService.Actions.UpdateError";
// update error extra
public static final String EXTRA_UPDATE_ERROR = "TemperatureUpdaterService.Extra.UpdateError";
private LocationManager mLocationManager;
private Looper mServiceLooper;
private Handler mServiceHandler;
private Boolean mIsUpdatingTemperature;
public static void startForUpdate(Context context) {
final Intent intent = new Intent(context, TemperatureUpdaterService.class);
intent.setAction(ACTION_UPDATE);
context.startService(intent);
}
@Override
public void onCreate() {
super.onCreate();
mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
HandlerThread thread = new HandlerThread("TemperatureUpdaterHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new Handler(mServiceLooper);
mIsUpdatingTemperature = false;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_UPDATE.equals(intent.getAction()) && !mIsUpdatingTemperature) {
if (ConnectivityUtils.isNetworkConnected(getApplicationContext())) {
mIsUpdatingTemperature = true;
// First get a new location
getNewLocation();
} else {
broadcastErrorAndStop(R.string.error_message_network_not_connected);
}
}
return START_REDELIVER_INTENT;
}
private void getNewLocation() {
final String provider = LocationUtils.getBestCoarseProvider(this);
if (provider == null) {
broadcastErrorAndStop(R.string.error_message_location_provider_not_found);
} else {
mLocationManager.requestSingleUpdate(provider, this, null);
}
}
private void updateTemperature(final Location location) {
mServiceHandler.post(new Runnable() {
@Override
public void run() {
try {
final URLConnection connection = getTemperatureURLConnection(location);
final OpenWeatherMapParserResult parserResult = parseURLConnection(connection);
PreferenceUtils.storeTemperatureInCelsius(TemperatureUpdaterService.this, parserResult.getTemperatureValue());
// Broadcast change to the widget provider
Intent intent = new Intent(TemperatureUpdaterService.this, STWidgetProvider.class);
intent.setAction(STWidgetProvider.APPWIDGET_DATA_CHANGED);
sendBroadcast(intent);
mIsUpdatingTemperature = false;
TemperatureUpdaterService.this.stopSelf();
} catch (SocketTimeoutException e) {
broadcastErrorAndStop(R.string.error_message_server_not_available);
} catch (MalformedURLException e) {
broadcastErrorAndStop(R.string.error_message_malformed_url);
} catch (IOException e) {
broadcastErrorAndStop(R.string.error_message_io_exception);
} catch (XmlPullParserException e) {
broadcastErrorAndStop(R.string.error_message_xml_pull_parser_exception);
}
}
});
}
private URLConnection getTemperatureURLConnection(Location location) throws IOException {
final URL url = new URL(getResources().getString(R.string.url_open_weather_api, location.getLatitude(), location.getLongitude()));
final URLConnection urlConnection = url.openConnection();
urlConnection.setConnectTimeout(10000);
urlConnection.setUseCaches(true);
return urlConnection;
}
private OpenWeatherMapParserResult parseURLConnection(URLConnection connection) throws IOException, XmlPullParserException {
final InputStream inputStream = new BufferedInputStream(connection.getInputStream());
final OpenWeatherMapParser parser = new OpenWeatherMapParser();
return parser.parse(inputStream);
}
private void broadcastErrorAndStop(int resourceId) {
final String message = getString(resourceId);
final Intent intent = new Intent(ACTION_UPDATE_ERROR);
intent.putExtra(EXTRA_UPDATE_ERROR, message);
sendBroadcast(intent);
stopSelf();
}
@Override
public void onLocationChanged(Location location) {
updateTemperature(location);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}