e0423d5f1b03668371befc57f3ad32125eb8bb18
[JavaForFun] /
1 package de.example.exampletdd.fragment.current;
2
3 import java.io.IOException;
4 import java.net.MalformedURLException;
5 import java.net.URISyntaxException;
6 import java.net.URL;
7 import java.text.DecimalFormat;
8 import java.text.NumberFormat;
9 import java.text.SimpleDateFormat;
10 import java.util.Calendar;
11 import java.util.Date;
12 import java.util.Locale;
13
14 import org.apache.http.client.ClientProtocolException;
15
16 import android.appwidget.AppWidgetManager;
17 import android.content.BroadcastReceiver;
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import android.content.SharedPreferences;
23 import android.graphics.Bitmap;
24 import android.graphics.BitmapFactory;
25 import android.net.http.AndroidHttpClient;
26 import android.os.AsyncTask;
27 import android.os.Bundle;
28 import android.preference.PreferenceManager;
29 import android.support.v4.app.Fragment;
30 import android.support.v4.content.LocalBroadcastManager;
31 import android.util.Log;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.widget.ImageView;
36 import android.widget.ProgressBar;
37 import android.widget.TextView;
38
39 import com.fasterxml.jackson.core.JsonParseException;
40
41 import de.example.exampletdd.R;
42 import de.example.exampletdd.WidgetIntentService;
43 import de.example.exampletdd.httpclient.CustomHTTPClient;
44 import de.example.exampletdd.model.DatabaseQueries;
45 import de.example.exampletdd.model.WeatherLocation;
46 import de.example.exampletdd.model.currentweather.Current;
47 import de.example.exampletdd.parser.JPOSWeatherParser;
48 import de.example.exampletdd.service.IconsList;
49 import de.example.exampletdd.service.PermanentStorage;
50 import de.example.exampletdd.service.ServiceParser;
51 import de.example.exampletdd.widget.WidgetProvider;
52
53 public class CurrentFragment extends Fragment {
54     private static final String TAG = "CurrentFragment";
55     private BroadcastReceiver mReceiver;
56
57     @Override
58     public void onCreate(final Bundle savedInstanceState) {
59         super.onCreate(savedInstanceState);
60     }
61
62     @Override
63     public View onCreateView(LayoutInflater inflater, ViewGroup container,
64                              Bundle savedInstanceState) {
65     
66         // Inflate the layout for this fragment
67         return inflater.inflate(R.layout.weather_current_fragment, container, false);
68     }
69     
70     @Override
71     public void onActivityCreated(final Bundle savedInstanceState) {
72         super.onActivityCreated(savedInstanceState);
73
74         if (savedInstanceState != null) {
75                 // Restore UI state
76             final Current current = (Current) savedInstanceState.getSerializable("Current");
77
78             // TODO: Could it be better to store in global forecast data even if it is null value?
79             //       So, perhaps do not check for null value and always store in global variable.
80             if (current != null) {
81                 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
82                 store.saveCurrent(current);
83             }
84         }     
85         
86         this.setHasOptionsMenu(false);
87
88         this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.GONE);
89         this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.VISIBLE);
90         this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.GONE);   
91     }
92
93     @Override
94     public void onResume() {
95         super.onResume();
96
97
98         this.mReceiver = new BroadcastReceiver() {
99
100                         @Override
101                         public void onReceive(final Context context, final Intent intent) {
102                                 final String action = intent.getAction();
103                                 if (action.equals("de.example.exampletdd.UPDATECURRENT")) {
104                                         final Current currentRemote = (Current) intent.getSerializableExtra("current");
105
106                                         if (currentRemote != null) {
107
108                                                 // 1. Check conditions. They must be the same as the ones that triggered the AsyncTask.
109                                                 final DatabaseQueries query = new DatabaseQueries(context.getApplicationContext());
110                                     final WeatherLocation weatherLocation = query.queryDataBase();
111                                     final PermanentStorage store = new PermanentStorage(context.getApplicationContext());
112                                     final Current current = store.getCurrent();
113
114                                     if (current == null || !CurrentFragment.this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
115                                         // 2. Update UI.
116                                         CurrentFragment.this.updateUI(currentRemote);
117
118                                         // 3. Update current data.
119                                                         store.saveCurrent(currentRemote);
120
121                             // 4. If is new data (new location) update widgets.
122                             if (weatherLocation.getIsNew()) {
123                                 final ComponentName widgets = new ComponentName(context.getApplicationContext(), WidgetProvider.class);
124                                 final AppWidgetManager manager = AppWidgetManager.getInstance(context.getApplicationContext());
125                                 final int[] appWidgetIds = manager.getAppWidgetIds(widgets);
126                                 for (final int appWidgetId : appWidgetIds) {
127                                     final Intent intentWidget = new Intent(context.getApplicationContext(), WidgetIntentService.class);
128                                     intentWidget.putExtra("updateByApp", true);
129                                     intentWidget.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
130                                     context.getApplicationContext().startService(intentWidget);
131                                 }
132                             }
133
134                             // 5. Update location data.
135                             weatherLocation.setIsNew(false);
136                             weatherLocation.setLastCurrentUIUpdate(new Date());
137                             query.updateDataBase(weatherLocation);
138                                     }
139
140                                         } else {
141                                                 // Empty UI and show error message
142                                                 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.GONE);
143                                                 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.GONE);
144                                                 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.VISIBLE);
145                                         }
146                                 }
147                         }
148         };
149
150         // Register receiver
151         final IntentFilter filter = new IntentFilter();
152         filter.addAction("de.example.exampletdd.UPDATECURRENT");
153         LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext())
154                                                         .registerReceiver(this.mReceiver, filter);
155
156         // Empty UI
157         this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.GONE);
158         
159         final DatabaseQueries query = new DatabaseQueries(this.getActivity().getApplicationContext());
160         final WeatherLocation weatherLocation = query.queryDataBase();
161         if (weatherLocation == null) {
162             // Nothing to do.
163                 // Show error message
164                 final ProgressBar progress = (ProgressBar) getActivity().findViewById(R.id.weather_current_progressbar);
165                 progress.setVisibility(View.GONE);
166                         final TextView errorMessage = (TextView) getActivity().findViewById(R.id.weather_current_error_message);
167                 errorMessage.setVisibility(View.VISIBLE);
168             return;
169         }
170
171         final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
172         final Current current = store.getCurrent();
173
174         if (current != null && this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
175             this.updateUI(current);
176         } else {
177             // Load remote data (aynchronous)
178             // Gets the data from the web.
179                 this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.VISIBLE);
180                 this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.GONE);
181             final CurrentTask task = new CurrentTask(
182                         this.getActivity().getApplicationContext(),
183                     new CustomHTTPClient(AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent")),
184                     new ServiceParser(new JPOSWeatherParser()));
185
186             task.execute(weatherLocation.getLatitude(), weatherLocation.getLongitude());
187             // TODO: make sure UI thread keeps running in parallel after that. I guess.
188         }
189     }
190
191     @Override
192     public void onSaveInstanceState(final Bundle savedInstanceState) {
193
194         // Save UI state
195         final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
196         final Current current = store.getCurrent();
197
198         // TODO: Could it be better to save current data even if it is null value?
199         //       So, perhaps do not check for null value.
200         if (current != null) {
201             savedInstanceState.putSerializable("Current", current);
202         }
203
204         super.onSaveInstanceState(savedInstanceState);
205     }
206
207     @Override
208     public void onPause() {
209         LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(this.mReceiver);
210
211         super.onPause();
212     }
213
214     private interface UnitsConversor {
215         
216         public double doConversion(final double value);
217     }
218
219     private void updateUI(final Current current) {
220         
221         final SharedPreferences sharedPreferences = PreferenceManager
222                 .getDefaultSharedPreferences(this.getActivity().getApplicationContext());
223
224         // TODO: repeating the same code in Overview, Specific and Current!!!
225         // 1. Update units of measurement.
226         // 1.1 Temperature
227         String tempSymbol;
228         UnitsConversor tempUnitsConversor;
229         String keyPreference = this.getResources().getString(R.string.weather_preferences_temperature_key);
230         String[] values = this.getResources().getStringArray(R.array.weather_preferences_temperature);
231         String unitsPreferenceValue = sharedPreferences.getString(
232                 keyPreference, this.getString(R.string.weather_preferences_temperature_celsius));
233         if (unitsPreferenceValue.equals(values[0])) {
234                 tempSymbol = values[0];
235                 tempUnitsConversor = new UnitsConversor(){
236
237                                 @Override
238                                 public double doConversion(final double value) {
239                                         return value - 273.15;
240                                 }
241                         
242                 };
243         } else if (unitsPreferenceValue.equals(values[1])) {
244                 tempSymbol = values[1];
245                 tempUnitsConversor = new UnitsConversor(){
246
247                                 @Override
248                                 public double doConversion(final double value) {
249                                         return (value * 1.8) - 459.67;
250                                 }
251                         
252                 };
253         } else {
254                 tempSymbol = values[2];
255                 tempUnitsConversor = new UnitsConversor(){
256
257                                 @Override
258                                 public double doConversion(final double value) {
259                                         return value;
260                                 }
261                         
262                 };
263         }
264
265         // 1.2 Wind
266         String windSymbol;
267         UnitsConversor windUnitsConversor;
268         keyPreference = this.getResources().getString(R.string.weather_preferences_wind_key);
269         values = this.getResources().getStringArray(R.array.weather_preferences_wind);
270         unitsPreferenceValue = sharedPreferences.getString(
271                 keyPreference, this.getString(R.string.weather_preferences_wind_meters));
272         if (unitsPreferenceValue.equals(values[0])) {
273                 windSymbol = values[0];
274                 windUnitsConversor = new UnitsConversor(){
275
276                         @Override
277                         public double doConversion(double value) {
278                                 return value;
279                         }       
280                 };
281         } else {
282                 windSymbol = values[1];
283                 windUnitsConversor = new UnitsConversor(){
284
285                         @Override
286                         public double doConversion(double value) {
287                                 return value * 2.237;
288                         }       
289                 };
290         }
291
292         // 1.3 Pressure
293         String pressureSymbol;
294         UnitsConversor pressureUnitsConversor;
295         keyPreference = this.getResources().getString(R.string.weather_preferences_pressure_key);
296         values = this.getResources().getStringArray(R.array.weather_preferences_pressure);
297         unitsPreferenceValue = sharedPreferences.getString(
298                 keyPreference, this.getString(R.string.weather_preferences_pressure_pascal));
299         if (unitsPreferenceValue.equals(values[0])) {
300                 pressureSymbol = values[0];
301                 pressureUnitsConversor = new UnitsConversor(){
302
303                         @Override
304                         public double doConversion(double value) {
305                                 return value;
306                         }       
307                 };
308         } else {
309                 pressureSymbol = values[1];
310                 pressureUnitsConversor = new UnitsConversor(){
311
312                         @Override
313                         public double doConversion(double value) {
314                                 return value / 113.25d;
315                         }       
316                 };
317         }
318
319
320         // 2. Formatters
321         final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
322         tempFormatter.applyPattern("#####.#####");
323         final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.US);
324
325         
326         // 3. Prepare data for UI.
327         String tempMax = "";
328         if (current.getMain().getTemp_max() != null) {
329             double conversion = (Double) current.getMain().getTemp_max();
330             conversion = tempUnitsConversor.doConversion(conversion);
331             tempMax = tempFormatter.format(conversion) + tempSymbol;
332         }
333         String tempMin = "";
334         if (current.getMain().getTemp_min() != null) {
335             double conversion = (Double) current.getMain().getTemp_min();
336             conversion = tempUnitsConversor.doConversion(conversion);
337             tempMin = tempFormatter.format(conversion) + tempSymbol;
338         }
339         Bitmap picture;
340         if ((current.getWeather().size() > 0)
341                 && (current.getWeather().get(0).getIcon() != null)
342                 && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
343             final String icon = current.getWeather().get(0).getIcon();
344             picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
345                     .getResourceDrawable());
346         } else {
347             picture = BitmapFactory.decodeResource(this.getResources(),
348                     R.drawable.weather_severe_alert);
349         }
350
351         // TODO: static resource
352         String description = "no description available";
353         if (current.getWeather().size() > 0) {
354             description = current.getWeather().get(0).getDescription();
355         }
356
357         // TODO: units!!!!
358         String humidityValue = "";
359         if ((current.getMain() != null)
360                 && (current.getMain().getHumidity() != null)) {
361             final double conversion = (Double) current.getMain().getHumidity();
362             humidityValue = tempFormatter.format(conversion);
363         }
364         String pressureValue = "";
365         if ((current.getMain() != null)
366                 && (current.getMain().getPressure() != null)) {
367             double conversion = (Double) current.getMain().getPressure();
368             conversion = pressureUnitsConversor.doConversion(conversion);
369             pressureValue = tempFormatter.format(conversion);
370         }
371         String windValue = "";
372         if ((current.getWind() != null)
373                 && (current.getWind().getSpeed() != null)) {
374             double conversion = (Double) current.getWind().getSpeed();
375             conversion = windUnitsConversor.doConversion(conversion);
376             windValue = tempFormatter.format(conversion);
377         }
378         String rainValue = "";
379         if ((current.getRain() != null)
380                 && (current.getRain().get3h() != null)) {
381             final double conversion = (Double) current.getRain().get3h();
382             rainValue = tempFormatter.format(conversion);
383         }
384         String cloudsValue = "";
385         if ((current.getClouds() != null)
386                 && (current.getClouds().getAll() != null)) {
387             final double conversion = (Double) current.getClouds().getAll();
388             cloudsValue = tempFormatter.format(conversion);
389         }
390         String snowValue = "";
391         if ((current.getSnow() != null)
392                 && (current.getSnow().get3h() != null)) {
393             final double conversion = (Double) current.getSnow().get3h();
394             snowValue = tempFormatter.format(conversion);
395         }
396         String feelsLike = "";
397         if (current.getMain().getTemp() != null) {
398             double conversion = (Double) current.getMain().getTemp();
399             conversion = tempUnitsConversor.doConversion(conversion);
400             feelsLike = tempFormatter.format(conversion);
401         }
402         String sunRiseTime = "";
403         if (current.getSys().getSunrise() != null) {
404             final long unixTime = (Long) current.getSys().getSunrise();
405             final Date unixDate = new Date(unixTime * 1000L);
406             sunRiseTime = dateFormat.format(unixDate);
407         }
408         String sunSetTime = "";
409         if (current.getSys().getSunset() != null) {
410             final long unixTime = (Long) current.getSys().getSunset();
411             final Date unixDate = new Date(unixTime * 1000L);
412             sunSetTime = dateFormat.format(unixDate);
413         }
414
415
416         // 4. Update UI.
417         final TextView tempMaxView = (TextView) getActivity().findViewById(R.id.weather_current_temp_max);
418         tempMaxView.setText(tempMax);
419         final TextView tempMinView = (TextView) getActivity().findViewById(R.id.weather_current_temp_min);
420         tempMinView.setText(tempMin);
421         final ImageView pictureView = (ImageView) getActivity().findViewById(R.id.weather_current_picture);
422         pictureView.setImageBitmap(picture);    
423         
424         final TextView descriptionView = (TextView) getActivity().findViewById(R.id.weather_current_description);
425         descriptionView.setText(description);
426         
427         ((TextView) getActivity().findViewById(R.id.weather_current_humidity_value)).setText(humidityValue);
428         ((TextView) getActivity().findViewById(R.id.weather_current_humidity_units)).setText(
429                         this.getActivity().getApplicationContext().getString(R.string.text_units_percent));
430         
431         ((TextView) getActivity().findViewById(R.id.weather_current_pressure_value)).setText(pressureValue);
432         ((TextView) getActivity().findViewById(R.id.weather_current_pressure_units)).setText(pressureSymbol);
433         
434         ((TextView) getActivity().findViewById(R.id.weather_current_wind_value)).setText(windValue);
435         ((TextView) getActivity().findViewById(R.id.weather_current_wind_units)).setText(windSymbol);
436         
437         ((TextView) getActivity().findViewById(R.id.weather_current_rain_value)).setText(rainValue);
438         ((TextView) getActivity().findViewById(R.id.weather_current_rain_units)).setText(
439                         this.getActivity().getApplicationContext().getString(R.string.text_units_mm3h));
440         
441         ((TextView) getActivity().findViewById(R.id.weather_current_clouds_value)).setText(cloudsValue);
442         ((TextView) getActivity().findViewById(R.id.weather_current_clouds_units)).setText(
443                         this.getActivity().getApplicationContext().getString(R.string.text_units_percent));
444         
445         ((TextView) getActivity().findViewById(R.id.weather_current_snow_value)).setText(snowValue);
446         ((TextView) getActivity().findViewById(R.id.weather_current_snow_units)).setText(
447                         this.getActivity().getApplicationContext().getString(R.string.text_units_mm3h));
448         
449         ((TextView) getActivity().findViewById(R.id.weather_current_feelslike_value)).setText(feelsLike);
450         ((TextView) getActivity().findViewById(R.id.weather_current_feelslike_units)).setText(tempSymbol);
451         
452         ((TextView) getActivity().findViewById(R.id.weather_current_sunrise_value)).setText(sunRiseTime);
453
454         ((TextView) getActivity().findViewById(R.id.weather_current_sunset_value)).setText(sunSetTime);
455         
456         this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.VISIBLE);
457         this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.GONE);
458         this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.GONE);       
459     }
460     
461     private boolean isDataFresh(final Date lastUpdate) {
462         if (lastUpdate == null) {
463                 return false;
464         }
465         
466         final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
467                         this.getActivity().getApplicationContext());
468         final String keyPreference = this.getString(R.string.weather_preferences_refresh_interval_key);
469         final String refresh = sharedPreferences.getString(
470                         keyPreference,
471                         this.getResources().getStringArray(R.array.weather_preferences_refresh_interval)[0]);
472         final Date currentTime = new Date();
473         if (((currentTime.getTime() - lastUpdate.getTime())) < Long.valueOf(refresh)) {
474                 return true;
475         }
476         
477         return false;
478     }
479     
480     // TODO: How could I show just one progress dialog when I have two fragments in tabs
481     //       activity doing the same in background?
482     //       I mean, if OverviewTask shows one progress dialog and CurrentTask does the same I will have
483     //       have two progress dialogs... How may I solve this problem? I HATE ANDROID.
484     private class CurrentTask extends AsyncTask<Object, Void, Current> {
485         // Store the context passed to the AsyncTask when the system instantiates it.
486         private final Context localContext;
487         final CustomHTTPClient HTTPClient;
488         final ServiceParser weatherService;
489
490         public CurrentTask(final Context context, final CustomHTTPClient HTTPClient,
491                         final ServiceParser weatherService) {
492                 this.localContext = context;
493             this.HTTPClient = HTTPClient;
494             this.weatherService = weatherService;
495         }
496
497         @Override
498         protected Current doInBackground(final Object... params) {
499                 final double latitude = (Double) params[0];
500             final double longitude = (Double) params[1];
501   
502             Current current = null;
503             try {
504                 current = this.doInBackgroundThrowable(latitude, longitude);
505             } catch (final JsonParseException e) {
506                 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
507             } catch (final ClientProtocolException e) {
508                 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
509             } catch (final MalformedURLException e) {
510                 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
511             } catch (final URISyntaxException e) {
512                 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
513             } catch (final IOException e) {
514                 // logger infrastructure swallows UnknownHostException :/
515                 Log.e(TAG, "CurrentTask doInBackground exception: " + e.getMessage(), e);
516             } finally {
517                 HTTPClient.close();
518             }
519
520             return current;
521         }
522
523         private Current doInBackgroundThrowable(final double latitude, final double longitude)
524                         throws URISyntaxException, ClientProtocolException, JsonParseException, IOException {
525
526                 final String APIVersion = localContext.getResources().getString(R.string.api_version);
527             final String urlAPI = localContext.getResources().getString(R.string.uri_api_weather_today);
528             final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion, latitude, longitude);
529             final String urlWithoutCache = url.concat("&time=" + System.currentTimeMillis());
530             final String jsonData = HTTPClient.retrieveDataAsString(new URL(urlWithoutCache));
531             final Current current = weatherService.retrieveCurrentFromJPOS(jsonData);
532             // TODO: what is this for? I guess I could skip it :/
533             final Calendar now = Calendar.getInstance();
534             current.setDate(now.getTime());
535
536             return current;
537         }
538
539         @Override
540         protected void onPostExecute(final Current current) {
541                 // TODO: Is AsyncTask calling this method even when RunTimeException in doInBackground method?
542                 // I hope so, otherwise I must catch(Throwable) in doInBackground method :(
543
544             // Call updateUI on the UI thread.
545             final Intent currentData = new Intent("de.example.exampletdd.UPDATECURRENT");
546             currentData.putExtra("current", current);
547             LocalBroadcastManager.getInstance(this.localContext).sendBroadcastSync(currentData);
548         }
549     }
550 }