1 package name.gumartinm.weather.information.widget.service;
3 import android.app.IntentService;
4 import android.app.PendingIntent;
5 import android.appwidget.AppWidgetManager;
6 import android.content.Context;
7 import android.content.Intent;
8 import android.graphics.Bitmap;
9 import android.graphics.BitmapFactory;
10 import android.net.Uri;
11 import android.net.http.AndroidHttpClient;
12 import android.support.v4.app.TaskStackBuilder;
13 import android.util.Log;
14 import android.view.View;
15 import android.widget.RemoteViews;
17 import com.fasterxml.jackson.core.JsonParseException;
18 import name.gumartinm.weather.information.R;
19 import name.gumartinm.weather.information.httpclient.CustomHTTPClient;
20 import name.gumartinm.weather.information.model.DatabaseQueries;
21 import name.gumartinm.weather.information.model.WeatherLocation;
22 import name.gumartinm.weather.information.model.currentweather.Current;
23 import name.gumartinm.weather.information.parser.JPOSWeatherParser;
24 import name.gumartinm.weather.information.service.IconsList;
25 import name.gumartinm.weather.information.service.PermanentStorage;
26 import name.gumartinm.weather.information.service.ServiceParser;
27 import name.gumartinm.weather.information.widget.WidgetConfigure;
29 import org.apache.http.client.ClientProtocolException;
31 import java.io.IOException;
32 import java.net.MalformedURLException;
33 import java.net.URISyntaxException;
35 import java.text.DecimalFormat;
36 import java.text.NumberFormat;
37 import java.util.Date;
38 import java.util.Locale;
40 public class WidgetIntentService extends IntentService {
41 private static final String TAG = "WidgetIntentService";
42 private static final long UPDATE_TIME_RATE = 86400000L;
44 public WidgetIntentService() {
49 protected void onHandleIntent(final Intent intent) {
50 Log.i(TAG, "onHandleIntent");
51 final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
52 final boolean isRefreshAppWidget = intent.getBooleanExtra("refreshAppWidget", false);
54 if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
55 // Nothing to do. Something went wrong. Show error.
60 final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
61 final WeatherLocation weatherLocation = query.queryDataBase();
63 if (weatherLocation == null) {
64 // Nothing to do. Show error.
65 final RemoteViews view = this.makeErrorView(appWidgetId);
66 this.updateWidget(view, appWidgetId);
70 // TODO: improve this code. Too tired right now...
71 if (!isRefreshAppWidget) {
74 final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
75 final Current current = store.getWidgetCurrentData(appWidgetId);
76 if (current != null) {
78 view = this.makeView(current, weatherLocation, appWidgetId);
81 view = this.makeErrorView(appWidgetId);
83 this.updateWidget(view, appWidgetId);
87 final Current current = this.getRemoteCurrent(weatherLocation);
88 if (current != null) {
90 view = this.makeView(current, weatherLocation, appWidgetId);
92 final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
93 store.saveWidgetCurrentData(current, appWidgetId);
96 view = this.makeErrorView(appWidgetId);
98 this.updateWidget(view, appWidgetId);
102 public static void deleteWidgetCurrentData(final Context context, final int appWidgetId) {
103 final PermanentStorage store = new PermanentStorage(context.getApplicationContext());
105 store.removeWidgetCurrentData(appWidgetId);
108 private Current getRemoteCurrent(final WeatherLocation weatherLocation) {
110 final ServiceParser weatherService = new ServiceParser(new JPOSWeatherParser());
111 final CustomHTTPClient HTTPClient = new CustomHTTPClient(
112 AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent"));
115 return this.getRemoteCurrentThrowable(weatherLocation, HTTPClient, weatherService);
117 } catch (final JsonParseException e) {
118 Log.e(TAG, "doInBackground exception: ", e);
119 } catch (final ClientProtocolException e) {
120 Log.e(TAG, "doInBackground exception: ", e);
121 } catch (final MalformedURLException e) {
122 Log.e(TAG, "doInBackground exception: ", e);
123 } catch (final URISyntaxException e) {
124 Log.e(TAG, "doInBackground exception: ", e);
125 } catch (final IOException e) {
126 // logger infrastructure swallows UnknownHostException :/
127 Log.e(TAG, "doInBackground exception: " + e.getMessage(), e);
135 private Current getRemoteCurrentThrowable(final WeatherLocation weatherLocation,
136 final CustomHTTPClient HTTPClient, final ServiceParser weatherService)
137 throws ClientProtocolException, MalformedURLException, URISyntaxException,
138 JsonParseException, IOException {
140 final String APIVersion = this.getResources().getString(R.string.api_version);
142 final String urlAPI = this.getResources().getString(R.string.uri_api_weather_today);
143 final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion,
144 weatherLocation.getLatitude(), weatherLocation.getLongitude());
145 final String urlWithoutCache = url.concat("&time=" + System.currentTimeMillis());
146 final String jsonData = HTTPClient.retrieveDataAsString(new URL(urlWithoutCache));
148 return weatherService.retrieveCurrentFromJPOS(jsonData);
151 private interface UnitsConversor {
153 public double doConversion(final double value);
156 private RemoteViews makeView(final Current current, final WeatherLocation weatherLocation, final int appWidgetId) {
158 // TODO: repeating the same code in Overview, Specific and Current!!!
159 // 1. Update units of measurement.
161 UnitsConversor tempUnitsConversor;
162 String keyPreference = this.getApplicationContext().getString(R.string.widget_preferences_temperature_units_key);
163 String realKeyPreference = keyPreference + "_" + appWidgetId;
164 // What was saved to permanent storage (or default values if it is the first time)
165 final int tempValue = this.getSharedPreferences("WIDGET_PREFERENCES", Context.MODE_PRIVATE).getInt(realKeyPreference, 0);
166 final String tempSymbol = this.getResources().getStringArray(R.array.weather_preferences_temperature)[tempValue];
167 if (tempValue == 0) {
168 tempUnitsConversor = new UnitsConversor(){
171 public double doConversion(final double value) {
172 return value - 273.15;
176 } else if (tempValue == 1) {
177 tempUnitsConversor = new UnitsConversor(){
180 public double doConversion(final double value) {
181 return (value * 1.8) - 459.67;
186 tempUnitsConversor = new UnitsConversor(){
189 public double doConversion(final double value) {
197 // 2. Update country.
198 keyPreference = this.getApplicationContext().getString(R.string.widget_preferences_country_switch_key);
199 realKeyPreference = keyPreference + "_" + appWidgetId;
200 // What was saved to permanent storage (or default values if it is the first time)
201 final boolean isCountry = this.getSharedPreferences("WIDGET_PREFERENCES", Context.MODE_PRIVATE)
202 .getBoolean(realKeyPreference, false);
206 final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
207 tempFormatter.applyPattern("#####.#####");
210 // 4. Prepare data for RemoteViews.
212 if (current.getMain().getTemp_max() != null) {
213 double conversion = (Double) current.getMain().getTemp_max();
214 conversion = tempUnitsConversor.doConversion(conversion);
215 tempMax = tempFormatter.format(conversion) + tempSymbol;
218 if (current.getMain().getTemp_min() != null) {
219 double conversion = (Double) current.getMain().getTemp_min();
220 conversion = tempUnitsConversor.doConversion(conversion);
221 tempMin = tempFormatter.format(conversion) + tempSymbol;
224 if ((current.getWeather().size() > 0)
225 && (current.getWeather().get(0).getIcon() != null)
226 && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
227 final String icon = current.getWeather().get(0).getIcon();
228 picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
229 .getResourceDrawable());
231 picture = BitmapFactory.decodeResource(this.getResources(),
232 R.drawable.weather_severe_alert);
234 final String city = weatherLocation.getCity();
235 final String country = weatherLocation.getCountry();
237 // 5. Insert data in RemoteViews.
238 final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.appwidget);
239 remoteView.setImageViewBitmap(R.id.weather_appwidget_image, picture);
240 remoteView.setTextViewText(R.id.weather_appwidget_temperature_max, tempMax);
241 remoteView.setTextViewText(R.id.weather_appwidget_temperature_min, tempMin);
242 remoteView.setTextViewText(R.id.weather_appwidget_city, city);
244 remoteView.setViewVisibility(R.id.weather_appwidget_country, View.GONE);
246 // TODO: It is as if Android had a view cache. If I did not set VISIBLE value,
247 // the country field would be gone forever... :/
248 remoteView.setViewVisibility(R.id.weather_appwidget_country, View.VISIBLE);
249 remoteView.setTextViewText(R.id.weather_appwidget_country, country);
253 // 6. Activity launcher.
254 final Intent resultIntent = new Intent(this.getApplicationContext(), WidgetConfigure.class);
255 resultIntent.putExtra("actionFromUser", true);
256 resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
257 // resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK );
258 // From: http://stackoverflow.com/questions/4011178/multiple-instances-of-widget-only-updating-last-widget
259 final Uri data = Uri.withAppendedPath(Uri.parse("PAIN" + "://widget/id/") ,String.valueOf(appWidgetId));
260 resultIntent.setData(data);
262 final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
263 // Adds the back stack for the Intent (but not the Intent itself)
264 stackBuilder.addParentStack(WidgetConfigure.class);
265 // Adds the Intent that starts the Activity to the top of the stack
266 stackBuilder.addNextIntent(resultIntent);
267 final PendingIntent resultPendingIntent =
268 stackBuilder.getPendingIntent(
270 PendingIntent.FLAG_UPDATE_CURRENT
272 remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
273 // final PendingIntent resultPendingIntent = PendingIntent.getActivity(
274 // this.getApplicationContext(),
277 // PendingIntent.FLAG_UPDATE_CURRENT);
278 // remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
283 private RemoteViews makeErrorView(final int appWidgetId) {
284 final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.appwidget_error);
286 // 6. Activity launcher.
287 final Intent resultIntent = new Intent(this.getApplicationContext(), WidgetConfigure.class);
288 resultIntent.putExtra("actionFromUser", true);
289 resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
290 // resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK );
291 // From: http://stackoverflow.com/questions/4011178/multiple-instances-of-widget-only-updating-last-widget
292 final Uri data = Uri.withAppendedPath(Uri.parse("PAIN" + "://widget/id/") ,String.valueOf(appWidgetId));
293 resultIntent.setData(data);
295 final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
296 // Adds the back stack for the Intent (but not the Intent itself)
297 stackBuilder.addParentStack(WidgetConfigure.class);
298 // Adds the Intent that starts the Activity to the top of the stack
299 stackBuilder.addNextIntent(resultIntent);
300 final PendingIntent resultPendingIntent =
301 stackBuilder.getPendingIntent(
303 PendingIntent.FLAG_UPDATE_CURRENT
305 remoteView.setOnClickPendingIntent(R.id.weather_appwidget_error, resultPendingIntent);
306 // final PendingIntent resultPendingIntent = PendingIntent.getActivity(
307 // this.getApplicationContext(),
310 // PendingIntent.FLAG_UPDATE_CURRENT);
311 // remoteView.setOnClickPendingIntent(R.id.weather_appwidget_error, resultPendingIntent);
316 private void updateWidget(final RemoteViews remoteView, final int appWidgetId) {
318 final AppWidgetManager manager = AppWidgetManager.getInstance(this.getApplicationContext());
319 manager.updateAppWidget(appWidgetId, remoteView);
322 private boolean isDataFresh(final Date lastUpdate) {
323 if (lastUpdate == null) {
327 final Date currentTime = new Date();
328 if (((currentTime.getTime() - lastUpdate.getTime())) < UPDATE_TIME_RATE) {
335 // private void updateWidgets(final RemoteViews remoteView) {
337 // final ComponentName widgets = new ComponentName(this.getApplicationContext(), WidgetProvider.class);
338 // final AppWidgetManager manager = AppWidgetManager.getInstance(this.getApplicationContext());
339 // manager.updateAppWidget(widgets, remoteView);