61dbb9cefc839e11b865ce97fd5381d0e6dbf2ec
[JavaForFun] /
1 package name.gumartinm.weather.information.widget.service;
2
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;
16
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;
27 import name.gumartinm.weather.information.widget.WidgetConfigure;
28
29 import org.apache.http.client.ClientProtocolException;
30
31 import java.io.IOException;
32 import java.net.MalformedURLException;
33 import java.net.URISyntaxException;
34 import java.net.URL;
35 import java.text.DecimalFormat;
36 import java.text.NumberFormat;
37 import java.util.Date;
38 import java.util.Locale;
39
40 public class WidgetIntentService extends IntentService {
41         private static final String TAG = "WidgetIntentService";
42     private static final long UPDATE_TIME_RATE = 86400000L;
43
44         public WidgetIntentService() {
45                 super("WIS-Thread");
46         }
47
48         @Override
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);
53
54                 if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
55                         // Nothing to do. Something went wrong. Show error.
56                         return;
57                 }
58
59
60                 final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
61                 final WeatherLocation weatherLocation = query.queryDataBase();
62                 
63                 if (weatherLocation == null) {
64                         // Nothing to do. Show error.
65                         final RemoteViews view = this.makeErrorView(appWidgetId);
66                         this.updateWidget(view, appWidgetId);
67                         return;
68                 }
69
70         // TODO: improve this code. Too tired right now...
71                 if (!isRefreshAppWidget) {
72             RemoteViews view;
73
74             final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
75             final Current current = store.getWidgetCurrentData(appWidgetId);
76             if (current != null) {
77                 // Update UI.
78                 view = this.makeView(current, weatherLocation, appWidgetId);
79             } else {
80                 // Show error.
81                 view = this.makeErrorView(appWidgetId);
82             }
83             this.updateWidget(view, appWidgetId);
84                 } else {
85             RemoteViews view;
86
87             final Current current = this.getRemoteCurrent(weatherLocation);
88             if (current != null) {
89                 // Update UI.
90                 view = this.makeView(current, weatherLocation, appWidgetId);
91
92                 final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
93                 store.saveWidgetCurrentData(current, appWidgetId);
94             } else {
95                 // Show error.
96                 view = this.makeErrorView(appWidgetId);
97             }
98             this.updateWidget(view, appWidgetId);
99                 }
100         }
101
102     public static void deleteWidgetCurrentData(final Context context, final int appWidgetId) {
103         final PermanentStorage store = new PermanentStorage(context.getApplicationContext());
104
105         store.removeWidgetCurrentData(appWidgetId);
106     }
107
108         private Current getRemoteCurrent(final WeatherLocation weatherLocation) {
109
110                 final ServiceCurrentParser weatherService = new ServiceCurrentParser(new JPOSCurrentParser());
111                 final CustomHTTPClient HTTPClient = new CustomHTTPClient(
112                                 AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent"));
113
114                 try {
115                         return this.getRemoteCurrentThrowable(weatherLocation, HTTPClient, weatherService);
116
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);
128                 } finally {
129                         HTTPClient.close();
130                 }
131
132                 return null;
133         }
134
135         private Current getRemoteCurrentThrowable(final WeatherLocation weatherLocation,
136                         final CustomHTTPClient HTTPClient, final ServiceCurrentParser weatherService)
137                                         throws ClientProtocolException, MalformedURLException, URISyntaxException,
138                                         JsonParseException, IOException {
139
140                 final String APIVersion = this.getResources().getString(R.string.api_version);
141
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));
147
148                 return weatherService.retrieveCurrentFromJPOS(jsonData);
149         }
150
151     private interface UnitsConversor {
152         
153         public double doConversion(final double value);
154     }
155     
156         private RemoteViews makeView(final Current current, final WeatherLocation weatherLocation, final int appWidgetId) {
157
158                 // TODO: repeating the same code in Overview, Specific and Current!!!
159                 // 1. Update units of measurement.
160
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(){
169
170                                 @Override
171                                 public double doConversion(final double value) {
172                                         return value - 273.15;
173                                 }
174
175                 };
176         } else if (tempValue == 1) {
177                 tempUnitsConversor = new UnitsConversor(){
178
179                                 @Override
180                                 public double doConversion(final double value) {
181                                         return (value * 1.8) - 459.67;
182                                 }
183
184                 };
185         } else {
186                 tempUnitsConversor = new UnitsConversor(){
187
188                                 @Override
189                                 public double doConversion(final double value) {
190                                         return value;
191                                 }
192
193                 };
194         }
195
196
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);
203
204
205                 // 3. Formatters
206                 final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
207                 tempFormatter.applyPattern("#####.#####");
208
209
210                 // 4. Prepare data for RemoteViews.
211                 String tempMax = "";
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;
216                 }
217                 String tempMin = "";
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;
222                 }
223                 Bitmap picture;
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());
230                 } else {
231                         picture = BitmapFactory.decodeResource(this.getResources(),
232                                         R.drawable.weather_severe_alert);
233                 }
234                 final String city = weatherLocation.getCity();
235                 final String country = weatherLocation.getCountry();
236
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);
243         if (!isCountry) {
244             remoteView.setViewVisibility(R.id.weather_appwidget_country, View.GONE);
245         } else {
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);
250         }
251
252
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);
261
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(
269                                                 0,
270                                                 PendingIntent.FLAG_UPDATE_CURRENT
271                                                 );
272                 remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
273 //        final PendingIntent resultPendingIntent = PendingIntent.getActivity(
274 //                this.getApplicationContext(),
275 //                0,
276 //                resultIntent,
277 //                PendingIntent.FLAG_UPDATE_CURRENT);
278 //        remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
279                 
280                 return remoteView;
281         }
282         
283         private RemoteViews makeErrorView(final int appWidgetId) {
284                 final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.appwidget_error);
285
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);
294
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(
302                                                 0,
303                                                 PendingIntent.FLAG_UPDATE_CURRENT
304                                                 );
305         remoteView.setOnClickPendingIntent(R.id.weather_appwidget_error, resultPendingIntent);
306 //        final PendingIntent resultPendingIntent = PendingIntent.getActivity(
307 //                this.getApplicationContext(),
308 //                0,
309 //                resultIntent,
310 //                PendingIntent.FLAG_UPDATE_CURRENT);
311 //              remoteView.setOnClickPendingIntent(R.id.weather_appwidget_error, resultPendingIntent);
312
313                 return remoteView;
314         }
315
316         private void updateWidget(final RemoteViews remoteView, final int appWidgetId) {
317                 
318                 final AppWidgetManager manager = AppWidgetManager.getInstance(this.getApplicationContext());
319                 manager.updateAppWidget(appWidgetId, remoteView);
320         }
321
322     private boolean isDataFresh(final Date lastUpdate) {
323         if (lastUpdate == null) {
324             return false;
325         }
326
327         final Date currentTime = new Date();
328         if (((currentTime.getTime() - lastUpdate.getTime())) < UPDATE_TIME_RATE) {
329             return true;
330         }
331
332         return false;
333     }
334         
335 //      private void updateWidgets(final RemoteViews remoteView) {
336 //              
337 //              final ComponentName widgets = new ComponentName(this.getApplicationContext(), WidgetProvider.class);
338 //              final AppWidgetManager manager = AppWidgetManager.getInstance(this.getApplicationContext());
339 //              manager.updateAppWidget(widgets, remoteView);
340 //      }
341 }