1 package de.example.exampletdd.fragment.current;
3 import java.io.IOException;
4 import java.net.MalformedURLException;
5 import java.net.URISyntaxException;
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;
14 import org.apache.http.client.ClientProtocolException;
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;
37 import com.fasterxml.jackson.core.JsonParseException;
39 import de.example.exampletdd.R;
40 import de.example.exampletdd.WidgetIntentService;
41 import de.example.exampletdd.httpclient.CustomHTTPClient;
42 import de.example.exampletdd.model.DatabaseQueries;
43 import de.example.exampletdd.model.WeatherLocation;
44 import de.example.exampletdd.model.currentweather.Current;
45 import de.example.exampletdd.parser.JPOSWeatherParser;
46 import de.example.exampletdd.service.IconsList;
47 import de.example.exampletdd.service.PermanentStorage;
48 import de.example.exampletdd.service.ServiceParser;
50 public class CurrentFragment extends Fragment {
51 private static final String TAG = "CurrentFragment";
52 private BroadcastReceiver mReceiver;
55 public void onCreate(final Bundle savedInstanceState) {
56 super.onCreate(savedInstanceState);
60 public View onCreateView(LayoutInflater inflater, ViewGroup container,
61 Bundle savedInstanceState) {
63 // Inflate the layout for this fragment
64 return inflater.inflate(R.layout.weather_current_fragment, container, false);
68 public void onActivityCreated(final Bundle savedInstanceState) {
69 super.onActivityCreated(savedInstanceState);
71 if (savedInstanceState != null) {
73 final Current current = (Current) savedInstanceState.getSerializable("Current");
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);
83 this.setHasOptionsMenu(false);
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);
91 public void onResume() {
95 this.mReceiver = new BroadcastReceiver() {
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");
103 if (currentRemote != null) {
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();
111 if (current == null || !CurrentFragment.this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
113 CurrentFragment.this.updateUI(currentRemote);
116 store.saveCurrent(currentRemote);
117 weatherLocation.setLastCurrentUIUpdate(new Date());
118 query.updateDataBase(weatherLocation);
122 // Empty UI and show error message
123 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.GONE);
124 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.GONE);
125 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.VISIBLE);
132 final IntentFilter filter = new IntentFilter();
133 filter.addAction("de.example.exampletdd.UPDATECURRENT");
134 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext())
135 .registerReceiver(this.mReceiver, filter);
138 this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.GONE);
140 final DatabaseQueries query = new DatabaseQueries(this.getActivity().getApplicationContext());
141 final WeatherLocation weatherLocation = query.queryDataBase();
142 if (weatherLocation == null) {
144 // Show error message
145 final ProgressBar progress = (ProgressBar) getActivity().findViewById(R.id.weather_current_progressbar);
146 progress.setVisibility(View.GONE);
147 final TextView errorMessage = (TextView) getActivity().findViewById(R.id.weather_current_error_message);
148 errorMessage.setVisibility(View.VISIBLE);
152 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
153 final Current current = store.getCurrent();
155 if (current != null && this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
156 this.updateUI(current);
158 // Load remote data (aynchronous)
159 // Gets the data from the web.
160 this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.VISIBLE);
161 this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.GONE);
162 final CurrentTask task = new CurrentTask(
163 this.getActivity().getApplicationContext(),
164 new CustomHTTPClient(AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent")),
165 new ServiceParser(new JPOSWeatherParser()));
167 task.execute(weatherLocation.getLatitude(), weatherLocation.getLongitude());
168 // TODO: make sure UI thread keeps running in parallel after that. I guess.
173 public void onSaveInstanceState(final Bundle savedInstanceState) {
176 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
177 final Current current = store.getCurrent();
179 // TODO: Could it be better to save current data even if it is null value?
180 // So, perhaps do not check for null value.
181 if (current != null) {
182 savedInstanceState.putSerializable("Current", current);
185 super.onSaveInstanceState(savedInstanceState);
189 public void onPause() {
190 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(this.mReceiver);
195 private interface UnitsConversor {
197 public double doConversion(final double value);
200 private void updateUI(final Current current) {
202 final SharedPreferences sharedPreferences = PreferenceManager
203 .getDefaultSharedPreferences(this.getActivity().getApplicationContext());
205 // TODO: repeating the same code in Overview, Specific and Current!!!
206 // 1. Update units of measurement.
209 UnitsConversor tempUnitsConversor;
210 String keyPreference = this.getResources().getString(R.string.weather_preferences_temperature_key);
211 String[] values = this.getResources().getStringArray(R.array.weather_preferences_temperature);
212 String unitsPreferenceValue = sharedPreferences.getString(
213 keyPreference, this.getString(R.string.weather_preferences_temperature_celsius));
214 if (unitsPreferenceValue.equals(values[0])) {
215 tempSymbol = values[0];
216 tempUnitsConversor = new UnitsConversor(){
219 public double doConversion(final double value) {
220 return value - 273.15;
224 } else if (unitsPreferenceValue.equals(values[1])) {
225 tempSymbol = values[1];
226 tempUnitsConversor = new UnitsConversor(){
229 public double doConversion(final double value) {
230 return (value * 1.8) - 459.67;
235 tempSymbol = values[2];
236 tempUnitsConversor = new UnitsConversor(){
239 public double doConversion(final double value) {
248 UnitsConversor windUnitsConversor;
249 keyPreference = this.getResources().getString(R.string.weather_preferences_wind_key);
250 values = this.getResources().getStringArray(R.array.weather_preferences_wind);
251 unitsPreferenceValue = sharedPreferences.getString(
252 keyPreference, this.getString(R.string.weather_preferences_wind_meters));
253 if (unitsPreferenceValue.equals(values[0])) {
254 windSymbol = values[0];
255 windUnitsConversor = new UnitsConversor(){
258 public double doConversion(double value) {
263 windSymbol = values[1];
264 windUnitsConversor = new UnitsConversor(){
267 public double doConversion(double value) {
268 return value * 2.237;
274 String pressureSymbol;
275 UnitsConversor pressureUnitsConversor;
276 keyPreference = this.getResources().getString(R.string.weather_preferences_pressure_key);
277 values = this.getResources().getStringArray(R.array.weather_preferences_pressure);
278 unitsPreferenceValue = sharedPreferences.getString(
279 keyPreference, this.getString(R.string.weather_preferences_pressure_pascal));
280 if (unitsPreferenceValue.equals(values[0])) {
281 pressureSymbol = values[0];
282 pressureUnitsConversor = new UnitsConversor(){
285 public double doConversion(double value) {
290 pressureSymbol = values[1];
291 pressureUnitsConversor = new UnitsConversor(){
294 public double doConversion(double value) {
295 return value / 113.25d;
302 final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
303 tempFormatter.applyPattern("#####.#####");
304 final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.US);
307 // 3. Prepare data for UI.
309 if (current.getMain().getTemp_max() != null) {
310 double conversion = (Double) current.getMain().getTemp_max();
311 conversion = tempUnitsConversor.doConversion(conversion);
312 tempMax = tempFormatter.format(conversion) + tempSymbol;
315 if (current.getMain().getTemp_min() != null) {
316 double conversion = (Double) current.getMain().getTemp_min();
317 conversion = tempUnitsConversor.doConversion(conversion);
318 tempMin = tempFormatter.format(conversion) + tempSymbol;
321 if ((current.getWeather().size() > 0)
322 && (current.getWeather().get(0).getIcon() != null)
323 && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
324 final String icon = current.getWeather().get(0).getIcon();
325 picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
326 .getResourceDrawable());
328 picture = BitmapFactory.decodeResource(this.getResources(),
329 R.drawable.weather_severe_alert);
332 // TODO: static resource
333 String description = "no description available";
334 if (current.getWeather().size() > 0) {
335 description = current.getWeather().get(0).getDescription();
339 String humidityValue = "";
340 if ((current.getMain() != null)
341 && (current.getMain().getHumidity() != null)) {
342 final double conversion = (Double) current.getMain().getHumidity();
343 humidityValue = tempFormatter.format(conversion);
345 String pressureValue = "";
346 if ((current.getMain() != null)
347 && (current.getMain().getPressure() != null)) {
348 double conversion = (Double) current.getMain().getPressure();
349 conversion = pressureUnitsConversor.doConversion(conversion);
350 pressureValue = tempFormatter.format(conversion);
352 String windValue = "";
353 if ((current.getWind() != null)
354 && (current.getWind().getSpeed() != null)) {
355 double conversion = (Double) current.getWind().getSpeed();
356 conversion = windUnitsConversor.doConversion(conversion);
357 windValue = tempFormatter.format(conversion);
359 String rainValue = "";
360 if ((current.getRain() != null)
361 && (current.getRain().get3h() != null)) {
362 final double conversion = (Double) current.getRain().get3h();
363 rainValue = tempFormatter.format(conversion);
365 String cloudsValue = "";
366 if ((current.getClouds() != null)
367 && (current.getClouds().getAll() != null)) {
368 final double conversion = (Double) current.getClouds().getAll();
369 cloudsValue = tempFormatter.format(conversion);
371 String snowValue = "";
372 if ((current.getSnow() != null)
373 && (current.getSnow().get3h() != null)) {
374 final double conversion = (Double) current.getSnow().get3h();
375 snowValue = tempFormatter.format(conversion);
377 String feelsLike = "";
378 if (current.getMain().getTemp() != null) {
379 double conversion = (Double) current.getMain().getTemp();
380 conversion = tempUnitsConversor.doConversion(conversion);
381 feelsLike = tempFormatter.format(conversion);
383 String sunRiseTime = "";
384 if (current.getSys().getSunrise() != null) {
385 final long unixTime = (Long) current.getSys().getSunrise();
386 final Date unixDate = new Date(unixTime * 1000L);
387 sunRiseTime = dateFormat.format(unixDate);
389 String sunSetTime = "";
390 if (current.getSys().getSunset() != null) {
391 final long unixTime = (Long) current.getSys().getSunset();
392 final Date unixDate = new Date(unixTime * 1000L);
393 sunSetTime = dateFormat.format(unixDate);
398 final TextView tempMaxView = (TextView) getActivity().findViewById(R.id.weather_current_temp_max);
399 tempMaxView.setText(tempMax);
400 final TextView tempMinView = (TextView) getActivity().findViewById(R.id.weather_current_temp_min);
401 tempMinView.setText(tempMin);
402 final ImageView pictureView = (ImageView) getActivity().findViewById(R.id.weather_current_picture);
403 pictureView.setImageBitmap(picture);
405 final TextView descriptionView = (TextView) getActivity().findViewById(R.id.weather_current_description);
406 descriptionView.setText(description);
408 ((TextView) getActivity().findViewById(R.id.weather_current_humidity_value)).setText(humidityValue);
409 ((TextView) getActivity().findViewById(R.id.weather_current_humidity_units)).setText(
410 this.getActivity().getApplicationContext().getString(R.string.text_units_percent));
412 ((TextView) getActivity().findViewById(R.id.weather_current_pressure_value)).setText(pressureValue);
413 ((TextView) getActivity().findViewById(R.id.weather_current_pressure_units)).setText(pressureSymbol);
415 ((TextView) getActivity().findViewById(R.id.weather_current_wind_value)).setText(windValue);
416 ((TextView) getActivity().findViewById(R.id.weather_current_wind_units)).setText(windSymbol);
418 ((TextView) getActivity().findViewById(R.id.weather_current_rain_value)).setText(rainValue);
419 ((TextView) getActivity().findViewById(R.id.weather_current_rain_units)).setText(
420 this.getActivity().getApplicationContext().getString(R.string.text_units_mm3h));
422 ((TextView) getActivity().findViewById(R.id.weather_current_clouds_value)).setText(cloudsValue);
423 ((TextView) getActivity().findViewById(R.id.weather_current_clouds_units)).setText(
424 this.getActivity().getApplicationContext().getString(R.string.text_units_percent));
426 ((TextView) getActivity().findViewById(R.id.weather_current_snow_value)).setText(snowValue);
427 ((TextView) getActivity().findViewById(R.id.weather_current_snow_units)).setText(
428 this.getActivity().getApplicationContext().getString(R.string.text_units_mm3h));
430 ((TextView) getActivity().findViewById(R.id.weather_current_feelslike_value)).setText(feelsLike);
431 ((TextView) getActivity().findViewById(R.id.weather_current_feelslike_units)).setText(tempSymbol);
433 ((TextView) getActivity().findViewById(R.id.weather_current_sunrise_value)).setText(sunRiseTime);
435 ((TextView) getActivity().findViewById(R.id.weather_current_sunset_value)).setText(sunSetTime);
437 this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.VISIBLE);
438 this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.GONE);
439 this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.GONE);
442 private boolean isDataFresh(final Date lastUpdate) {
443 if (lastUpdate == null) {
447 final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
448 this.getActivity().getApplicationContext());
449 final String keyPreference = this.getString(R.string.weather_preferences_refresh_interval_key);
450 final String refresh = sharedPreferences.getString(
452 this.getResources().getStringArray(R.array.weather_preferences_refresh_interval)[0]);
453 final Date currentTime = new Date();
454 if (((currentTime.getTime() - lastUpdate.getTime())) < Long.valueOf(refresh)) {
461 // TODO: How could I show just one progress dialog when I have two fragments in tabs
462 // activity doing the same in background?
463 // I mean, if OverviewTask shows one progress dialog and CurrentTask does the same I will have
464 // have two progress dialogs... How may I solve this problem? I HATE ANDROID.
465 private class CurrentTask extends AsyncTask<Object, Void, Current> {
466 // Store the context passed to the AsyncTask when the system instantiates it.
467 private final Context localContext;
468 final CustomHTTPClient HTTPClient;
469 final ServiceParser weatherService;
471 public CurrentTask(final Context context, final CustomHTTPClient HTTPClient,
472 final ServiceParser weatherService) {
473 this.localContext = context;
474 this.HTTPClient = HTTPClient;
475 this.weatherService = weatherService;
479 protected Current doInBackground(final Object... params) {
480 final double latitude = (Double) params[0];
481 final double longitude = (Double) params[1];
483 Current current = null;
485 current = this.doInBackgroundThrowable(latitude, longitude);
486 } catch (final JsonParseException e) {
487 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
488 } catch (final ClientProtocolException e) {
489 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
490 } catch (final MalformedURLException e) {
491 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
492 } catch (final URISyntaxException e) {
493 Log.e(TAG, "CurrentTask doInBackground exception: ", e);
494 } catch (final IOException e) {
495 // logger infrastructure swallows UnknownHostException :/
496 Log.e(TAG, "CurrentTask doInBackground exception: " + e.getMessage(), e);
504 private Current doInBackgroundThrowable(final double latitude, final double longitude)
505 throws URISyntaxException, ClientProtocolException, JsonParseException, IOException {
507 final String APIVersion = localContext.getResources().getString(R.string.api_version);
508 final String urlAPI = localContext.getResources().getString(R.string.uri_api_weather_today);
509 final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion, latitude, longitude);
510 final String urlWithoutCache = url.concat("&time=" + System.currentTimeMillis());
511 final String jsonData = HTTPClient.retrieveDataAsString(new URL(urlWithoutCache));
512 final Current current = weatherService.retrieveCurrentFromJPOS(jsonData);
513 // TODO: what is this for? I guess I could skip it :/
514 final Calendar now = Calendar.getInstance();
515 current.setDate(now.getTime());
521 protected void onPostExecute(final Current current) {
522 // TODO: Is AsyncTask calling this method even when RunTimeException in doInBackground method?
523 // I hope so, otherwise I must catch(Throwable) in doInBackground method :(
525 // Call updateUI on the UI thread.
526 final Intent currentData = new Intent("de.example.exampletdd.UPDATECURRENT");
527 currentData.putExtra("current", current);
528 LocalBroadcastManager.getInstance(this.localContext).sendBroadcastSync(currentData);