1 package name.gumartinm.weather.information.widget;
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.JPOSCurrentParser;
24 import name.gumartinm.weather.information.service.IconsList;
25 import name.gumartinm.weather.information.service.PermanentStorage;
26 import name.gumartinm.weather.information.service.ServiceCurrentParser;
28 import org.apache.http.client.ClientProtocolException;
30 import java.io.IOException;
31 import java.net.MalformedURLException;
32 import java.net.URISyntaxException;
34 import java.text.DecimalFormat;
35 import java.text.NumberFormat;
36 import java.util.Date;
37 import java.util.Locale;
39 public class WidgetIntentService extends IntentService {
40 private static final String TAG = "WidgetIntentService";
41 private static final long UPDATE_TIME_RATE = 86400000L;
43 public WidgetIntentService() {
48 protected void onHandleIntent(final Intent intent) {
49 Log.i(TAG, "onHandleIntent");
50 final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
51 final boolean isRefreshAppWidget = intent.getBooleanExtra("refreshAppWidget", false);
53 if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
54 // Nothing to do. Something went wrong. Show error.
59 final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
60 final WeatherLocation weatherLocation = query.queryDataBase();
62 if (weatherLocation == null) {
63 // Nothing to do. Show error.
64 final RemoteViews view = this.makeErrorView(appWidgetId);
65 this.updateWidget(view, appWidgetId);
69 // TODO: improve this code. Too tired right now...
70 if (!isRefreshAppWidget) {
73 final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
74 final Current current = store.getWidgetCurrentData(appWidgetId);
75 if (current != null) {
77 view = this.makeView(current, weatherLocation, appWidgetId);
80 view = this.makeErrorView(appWidgetId);
82 this.updateWidget(view, appWidgetId);
86 final Current current = this.getRemoteCurrent(weatherLocation);
87 if (current != null) {
89 view = this.makeView(current, weatherLocation, appWidgetId);
91 final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
92 store.saveWidgetCurrentData(current, appWidgetId);
95 view = this.makeErrorView(appWidgetId);
97 this.updateWidget(view, appWidgetId);
101 public static void deleteWidgetCurrentData(final Context context, final int appWidgetId) {
102 final PermanentStorage store = new PermanentStorage(context.getApplicationContext());
104 store.removeWidgetCurrentData(appWidgetId);
107 private Current getRemoteCurrent(final WeatherLocation weatherLocation) {
109 final ServiceCurrentParser weatherService = new ServiceCurrentParser(new JPOSCurrentParser());
110 final CustomHTTPClient HTTPClient = new CustomHTTPClient(
111 AndroidHttpClient.newInstance(this.getString(R.string.http_client_agent)));
114 return this.getRemoteCurrentThrowable(weatherLocation, HTTPClient, weatherService);
116 } catch (final JsonParseException e) {
117 Log.e(TAG, "doInBackground exception: ", e);
118 } catch (final ClientProtocolException e) {
119 Log.e(TAG, "doInBackground exception: ", e);
120 } catch (final MalformedURLException e) {
121 Log.e(TAG, "doInBackground exception: ", e);
122 } catch (final URISyntaxException e) {
123 Log.e(TAG, "doInBackground exception: ", e);
124 } catch (final IOException e) {
125 // logger infrastructure swallows UnknownHostException :/
126 Log.e(TAG, "doInBackground exception: " + e.getMessage(), e);
134 private Current getRemoteCurrentThrowable(final WeatherLocation weatherLocation,
135 final CustomHTTPClient HTTPClient, final ServiceCurrentParser weatherService)
136 throws ClientProtocolException, MalformedURLException, URISyntaxException,
137 JsonParseException, IOException {
139 final String APIVersion = this.getResources().getString(R.string.api_version);
141 final String urlAPI = this.getResources().getString(R.string.uri_api_weather_today);
142 final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion,
143 weatherLocation.getLatitude(), weatherLocation.getLongitude());
144 final String urlWithoutCache = url.concat("&time=" + System.currentTimeMillis());
145 final String jsonData = HTTPClient.retrieveDataAsString(new URL(urlWithoutCache));
147 return weatherService.retrieveCurrentFromJPOS(jsonData);
150 private interface UnitsConversor {
152 public double doConversion(final double value);
155 private RemoteViews makeView(final Current current, final WeatherLocation weatherLocation, final int appWidgetId) {
157 // TODO: repeating the same code in Overview, Specific and Current!!!
158 // 1. Update units of measurement.
160 UnitsConversor tempUnitsConversor;
161 String keyPreference = this.getApplicationContext().getString(R.string.widget_preferences_temperature_units_key);
162 String realKeyPreference = keyPreference + "_" + appWidgetId;
163 // What was saved to permanent storage (or default values if it is the first time)
164 final int tempValue = this.getSharedPreferences("WIDGET_PREFERENCES", Context.MODE_PRIVATE).getInt(realKeyPreference, 0);
165 final String tempSymbol = this.getResources().getStringArray(R.array.weather_preferences_temperature)[tempValue];
166 if (tempValue == 0) {
167 tempUnitsConversor = new UnitsConversor(){
170 public double doConversion(final double value) {
171 return value - 273.15;
175 } else if (tempValue == 1) {
176 tempUnitsConversor = new UnitsConversor(){
179 public double doConversion(final double value) {
180 return (value * 1.8) - 459.67;
185 tempUnitsConversor = new UnitsConversor(){
188 public double doConversion(final double value) {
196 // 2. Update country.
197 keyPreference = this.getApplicationContext().getString(R.string.widget_preferences_country_switch_key);
198 realKeyPreference = keyPreference + "_" + appWidgetId;
199 // What was saved to permanent storage (or default values if it is the first time)
200 final boolean isCountry = this.getSharedPreferences("WIDGET_PREFERENCES", Context.MODE_PRIVATE)
201 .getBoolean(realKeyPreference, false);
205 final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
206 tempFormatter.applyPattern("#####.#####");
209 // 4. Prepare data for RemoteViews.
211 if (current.getMain().getTemp_max() != null) {
212 double conversion = (Double) current.getMain().getTemp_max();
213 conversion = tempUnitsConversor.doConversion(conversion);
214 tempMax = tempFormatter.format(conversion) + tempSymbol;
217 if (current.getMain().getTemp_min() != null) {
218 double conversion = (Double) current.getMain().getTemp_min();
219 conversion = tempUnitsConversor.doConversion(conversion);
220 tempMin = tempFormatter.format(conversion) + tempSymbol;
223 if ((current.getWeather().size() > 0)
224 && (current.getWeather().get(0).getIcon() != null)
225 && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
226 final String icon = current.getWeather().get(0).getIcon();
227 picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
228 .getResourceDrawable());
230 picture = BitmapFactory.decodeResource(this.getResources(),
231 R.drawable.weather_severe_alert);
233 final String city = weatherLocation.getCity();
234 final String country = weatherLocation.getCountry();
236 // 5. Insert data in RemoteViews.
237 final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.appwidget);
238 remoteView.setImageViewBitmap(R.id.weather_appwidget_image, picture);
239 remoteView.setTextViewText(R.id.weather_appwidget_temperature_max, tempMax);
240 remoteView.setTextViewText(R.id.weather_appwidget_temperature_min, tempMin);
241 remoteView.setTextViewText(R.id.weather_appwidget_city, city);
243 remoteView.setViewVisibility(R.id.weather_appwidget_country, View.GONE);
245 // TODO: It is as if Android had a view cache. If I did not set VISIBLE value,
246 // the country field would be gone forever... :/
247 remoteView.setViewVisibility(R.id.weather_appwidget_country, View.VISIBLE);
248 remoteView.setTextViewText(R.id.weather_appwidget_country, country);
252 // 6. Activity launcher.
253 final Intent resultIntent = new Intent(this.getApplicationContext(), WidgetConfigure.class);
254 resultIntent.putExtra("actionFromUser", true);
255 resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
256 // resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK );
257 // From: http://stackoverflow.com/questions/4011178/multiple-instances-of-widget-only-updating-last-widget
258 final Uri data = Uri.withAppendedPath(Uri.parse("PAIN" + "://widget/id/") ,String.valueOf(appWidgetId));
259 resultIntent.setData(data);
261 final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
262 // Adds the back stack for the Intent (but not the Intent itself)
263 stackBuilder.addParentStack(WidgetConfigure.class);
264 // Adds the Intent that starts the Activity to the top of the stack
265 stackBuilder.addNextIntent(resultIntent);
266 final PendingIntent resultPendingIntent =
267 stackBuilder.getPendingIntent(
269 PendingIntent.FLAG_UPDATE_CURRENT
271 remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
272 // final PendingIntent resultPendingIntent = PendingIntent.getActivity(
273 // this.getApplicationContext(),
276 // PendingIntent.FLAG_UPDATE_CURRENT);
277 // remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
282 private RemoteViews makeErrorView(final int appWidgetId) {
283 final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.appwidget_error);
285 // 6. Activity launcher.
286 final Intent resultIntent = new Intent(this.getApplicationContext(), WidgetConfigure.class);
287 resultIntent.putExtra("actionFromUser", true);
288 resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
289 // resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK );
290 // From: http://stackoverflow.com/questions/4011178/multiple-instances-of-widget-only-updating-last-widget
291 final Uri data = Uri.withAppendedPath(Uri.parse("PAIN" + "://widget/id/") ,String.valueOf(appWidgetId));
292 resultIntent.setData(data);
294 final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
295 // Adds the back stack for the Intent (but not the Intent itself)
296 stackBuilder.addParentStack(WidgetConfigure.class);
297 // Adds the Intent that starts the Activity to the top of the stack
298 stackBuilder.addNextIntent(resultIntent);
299 final PendingIntent resultPendingIntent =
300 stackBuilder.getPendingIntent(
302 PendingIntent.FLAG_UPDATE_CURRENT
304 remoteView.setOnClickPendingIntent(R.id.weather_appwidget_error, resultPendingIntent);
305 // final PendingIntent resultPendingIntent = PendingIntent.getActivity(
306 // this.getApplicationContext(),
309 // PendingIntent.FLAG_UPDATE_CURRENT);
310 // remoteView.setOnClickPendingIntent(R.id.weather_appwidget_error, resultPendingIntent);
315 private void updateWidget(final RemoteViews remoteView, final int appWidgetId) {
317 final AppWidgetManager manager = AppWidgetManager.getInstance(this.getApplicationContext());
318 manager.updateAppWidget(appWidgetId, remoteView);
321 // private void updateWidgets(final RemoteViews remoteView) {
323 // final ComponentName widgets = new ComponentName(this.getApplicationContext(), WidgetProvider.class);
324 // final AppWidgetManager manager = AppWidgetManager.getInstance(this.getApplicationContext());
325 // manager.updateAppWidget(widgets, remoteView);