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