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.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;
39 import com.fasterxml.jackson.core.JsonParseException;
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;
53 public class CurrentFragment extends Fragment {
54 private static final String TAG = "CurrentFragment";
55 private BroadcastReceiver mReceiver;
58 public void onCreate(final Bundle savedInstanceState) {
59 super.onCreate(savedInstanceState);
63 public View onCreateView(LayoutInflater inflater, ViewGroup container,
64 Bundle savedInstanceState) {
66 // Inflate the layout for this fragment
67 return inflater.inflate(R.layout.weather_current_fragment, container, false);
71 public void onActivityCreated(final Bundle savedInstanceState) {
72 super.onActivityCreated(savedInstanceState);
74 if (savedInstanceState != null) {
76 final Current current = (Current) savedInstanceState.getSerializable("Current");
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);
86 this.setHasOptionsMenu(false);
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);
94 public void onResume() {
98 this.mReceiver = new BroadcastReceiver() {
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");
106 if (currentRemote != null) {
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();
114 if (current == null || !CurrentFragment.this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
116 CurrentFragment.this.updateUI(currentRemote);
118 // 3. Update current data.
119 store.saveCurrent(currentRemote);
121 // 4. If is new data (new location) update widgets.
122 if (weatherLocation.getIsNew()) {
123 WidgetProvider.updateAllAppWidgets(context);
126 // 5. Update location data.
127 weatherLocation.setIsNew(false);
128 weatherLocation.setLastCurrentUIUpdate(new Date());
129 query.updateDataBase(weatherLocation);
133 // Empty UI and show error message
134 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.GONE);
135 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_progressbar).setVisibility(View.GONE);
136 CurrentFragment.this.getActivity().findViewById(R.id.weather_current_error_message).setVisibility(View.VISIBLE);
143 final IntentFilter filter = new IntentFilter();
144 filter.addAction("de.example.exampletdd.UPDATECURRENT");
145 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext())
146 .registerReceiver(this.mReceiver, filter);
149 this.getActivity().findViewById(R.id.weather_current_data_container).setVisibility(View.GONE);
151 final DatabaseQueries query = new DatabaseQueries(this.getActivity().getApplicationContext());
152 final WeatherLocation weatherLocation = query.queryDataBase();
153 if (weatherLocation == null) {
155 // Show error message
156 final ProgressBar progress = (ProgressBar) getActivity().findViewById(R.id.weather_current_progressbar);
157 progress.setVisibility(View.GONE);
158 final TextView errorMessage = (TextView) getActivity().findViewById(R.id.weather_current_error_message);
159 errorMessage.setVisibility(View.VISIBLE);
163 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
164 final Current current = store.getCurrent();
166 if (current != null && this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
167 this.updateUI(current);
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()));
178 task.execute(weatherLocation.getLatitude(), weatherLocation.getLongitude());
179 // TODO: make sure UI thread keeps running in parallel after that. I guess.
184 public void onSaveInstanceState(final Bundle savedInstanceState) {
187 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
188 final Current current = store.getCurrent();
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);
196 super.onSaveInstanceState(savedInstanceState);
200 public void onPause() {
201 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(this.mReceiver);
206 private interface UnitsConversor {
208 public double doConversion(final double value);
211 private void updateUI(final Current current) {
213 final SharedPreferences sharedPreferences = PreferenceManager
214 .getDefaultSharedPreferences(this.getActivity().getApplicationContext());
216 // TODO: repeating the same code in Overview, Specific and Current!!!
217 // 1. Update units of measurement.
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(){
230 public double doConversion(final double value) {
231 return value - 273.15;
235 } else if (unitsPreferenceValue.equals(values[1])) {
236 tempSymbol = values[1];
237 tempUnitsConversor = new UnitsConversor(){
240 public double doConversion(final double value) {
241 return (value * 1.8) - 459.67;
246 tempSymbol = values[2];
247 tempUnitsConversor = new UnitsConversor(){
250 public double doConversion(final double value) {
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(){
269 public double doConversion(double value) {
274 windSymbol = values[1];
275 windUnitsConversor = new UnitsConversor(){
278 public double doConversion(double value) {
279 return value * 2.237;
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(){
296 public double doConversion(double value) {
301 pressureSymbol = values[1];
302 pressureUnitsConversor = new UnitsConversor(){
305 public double doConversion(double value) {
306 return value / 113.25d;
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);
318 // 3. Prepare data for UI.
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;
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;
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());
339 picture = BitmapFactory.decodeResource(this.getResources(),
340 R.drawable.weather_severe_alert);
343 // TODO: static resource
344 String description = "no description available";
345 if (current.getWeather().size() > 0) {
346 description = current.getWeather().get(0).getDescription();
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
416 final TextView descriptionView = (TextView) getActivity().findViewById(R.id.weather_current_description);
417 descriptionView.setText(description);
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));
423 ((TextView) getActivity().findViewById(R.id.weather_current_pressure_value)).setText(pressureValue);
424 ((TextView) getActivity().findViewById(R.id.weather_current_pressure_units)).setText(pressureSymbol);
426 ((TextView) getActivity().findViewById(R.id.weather_current_wind_value)).setText(windValue);
427 ((TextView) getActivity().findViewById(R.id.weather_current_wind_units)).setText(windSymbol);
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));
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));
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));
441 ((TextView) getActivity().findViewById(R.id.weather_current_feelslike_value)).setText(feelsLike);
442 ((TextView) getActivity().findViewById(R.id.weather_current_feelslike_units)).setText(tempSymbol);
444 ((TextView) getActivity().findViewById(R.id.weather_current_sunrise_value)).setText(sunRiseTime);
446 ((TextView) getActivity().findViewById(R.id.weather_current_sunset_value)).setText(sunSetTime);
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);
453 private boolean isDataFresh(final Date lastUpdate) {
454 if (lastUpdate == null) {
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(
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)) {
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;
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;
490 protected Current doInBackground(final Object... params) {
491 final double latitude = (Double) params[0];
492 final double longitude = (Double) params[1];
494 Current current = null;
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);
515 private Current doInBackgroundThrowable(final double latitude, final double longitude)
516 throws URISyntaxException, ClientProtocolException, JsonParseException, IOException {
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());
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 :(
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);