568b0293748aa92109530df23426e5f57ec47ece
[JavaForFun] /
1 package com.weather.information.fragment.overview;
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.ArrayList;
11 import java.util.Calendar;
12 import java.util.Date;
13 import java.util.List;
14 import java.util.Locale;
15
16 import org.apache.http.client.ClientProtocolException;
17
18 import android.content.BroadcastReceiver;
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.SharedPreferences;
24 import android.graphics.Bitmap;
25 import android.graphics.BitmapFactory;
26 import android.net.http.AndroidHttpClient;
27 import android.os.AsyncTask;
28 import android.os.Bundle;
29 import android.preference.PreferenceManager;
30 import android.support.v4.app.ListFragment;
31 import android.support.v4.content.LocalBroadcastManager;
32 import android.util.Log;
33 import android.view.View;
34 import android.widget.ListView;
35
36 import com.fasterxml.jackson.core.JsonParseException;
37 import com.weather.information.R;
38 import com.weather.information.fragment.specific.SpecificFragment;
39 import com.weather.information.httpclient.CustomHTTPClient;
40 import com.weather.information.model.DatabaseQueries;
41 import com.weather.information.model.WeatherLocation;
42 import com.weather.information.model.forecastweather.Forecast;
43 import com.weather.information.parser.JPOSWeatherParser;
44 import com.weather.information.service.IconsList;
45 import com.weather.information.service.PermanentStorage;
46 import com.weather.information.service.ServiceParser;
47
48 public class OverviewFragment extends ListFragment {
49     private static final String TAG = "OverviewFragment";
50     private BroadcastReceiver mReceiver;
51
52     @Override
53     public void onCreate(final Bundle savedInstanceState) {
54         super.onCreate(savedInstanceState);
55     }
56
57     @Override
58     public void onActivityCreated(final Bundle savedInstanceState) {
59         super.onActivityCreated(savedInstanceState);
60
61         final ListView listWeatherView = this.getListView();
62         listWeatherView.setChoiceMode(ListView.CHOICE_MODE_NONE);
63
64         if (savedInstanceState != null) {
65             // Restore UI state
66             final Forecast forecast = (Forecast) savedInstanceState.getSerializable("Forecast");
67
68             if (forecast != null) {
69                 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
70                 store.saveForecast(forecast);
71             }
72         }
73
74         this.setHasOptionsMenu(false);
75
76         this.setEmptyText(this.getString(R.string.text_field_remote_error));
77         this.setListShownNoAnimation(false);
78     }
79
80     @Override
81     public void onResume() {
82         super.onResume();
83
84         this.mReceiver = new BroadcastReceiver() {
85
86                         @Override
87                         public void onReceive(final Context context, final Intent intent) {
88                                 final String action = intent.getAction();
89                                 if (action.equals("com.weather.information.UPDATEFORECAST")) {
90                                         final Forecast forecastRemote = (Forecast) intent.getSerializableExtra("forecast");
91
92                                         if (forecastRemote != null) {
93
94                                                 // 1. Check conditions. They must be the same as the ones that triggered the AsyncTask.
95                                                 final DatabaseQueries query = new DatabaseQueries(context.getApplicationContext());
96                                     final WeatherLocation weatherLocation = query.queryDataBase();
97                                     final PermanentStorage store = new PermanentStorage(context.getApplicationContext());
98                                     final Forecast forecast = store.getForecast();
99
100                                         if (forecast == null || !OverviewFragment.this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
101                                                 // 2. Update UI.
102                                                 OverviewFragment.this.updateUI(forecastRemote);
103
104                                                 // 3. Update Data.
105                                                 store.saveForecast(forecastRemote);
106                                             weatherLocation.setLastForecastUIUpdate(new Date());
107                                             query.updateDataBase(weatherLocation);
108
109                                             // 4. Show list.
110                                             OverviewFragment.this.setListShownNoAnimation(true);
111                                         }
112
113                                         } else {
114                                                 // Empty list and show error message (see setEmptyText in onCreate)
115                                                 OverviewFragment.this.setListAdapter(null);
116                                                 OverviewFragment.this.setListShownNoAnimation(true);
117                                         }
118                                 }
119                         }
120         };
121
122         // Register receiver
123         final IntentFilter filter = new IntentFilter();
124         filter.addAction("com.weather.information.UPDATEFORECAST");
125         LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext())
126                                                         .registerReceiver(this.mReceiver, filter);
127
128         final DatabaseQueries query = new DatabaseQueries(this.getActivity().getApplicationContext());
129         final WeatherLocation weatherLocation = query.queryDataBase();
130         if (weatherLocation == null) {
131             // Nothing to do.
132                 // Empty list and show error message (see setEmptyText in onCreate)
133                         this.setListAdapter(null);
134                         this.setListShownNoAnimation(true);
135             return;
136         }
137
138         final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
139         final Forecast forecast = store.getForecast();
140
141         if (forecast != null && this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
142             this.updateUI(forecast);
143         } else {
144             // Load remote data (aynchronous)
145             // Gets the data from the web.
146             this.setListShownNoAnimation(false);
147             final OverviewTask task = new OverviewTask(
148                         this.getActivity().getApplicationContext(),
149                     new CustomHTTPClient(AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent")),
150                     new ServiceParser(new JPOSWeatherParser()));
151
152             task.execute(weatherLocation.getLatitude(), weatherLocation.getLongitude());
153         }
154     }
155
156     @Override
157     public void onSaveInstanceState(final Bundle savedInstanceState) {
158
159         // Save UI state
160         final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
161         final Forecast forecast = store.getForecast();
162
163         if (forecast != null) {
164             savedInstanceState.putSerializable("Forecast", forecast);
165         }
166
167         super.onSaveInstanceState(savedInstanceState);
168     }
169
170     @Override
171     public void onPause() {
172         LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(this.mReceiver);
173
174         super.onPause();
175     }
176
177     @Override
178     public void onListItemClick(final ListView l, final View v, final int position, final long id) {
179         final SpecificFragment fragment = (SpecificFragment) this
180                 .getFragmentManager().findFragmentById(R.id.weather_specific_fragment);
181         if (fragment == null) {
182             // handset layout
183             final Intent intent = new Intent("com.weather.information.WEATHERINFO")
184             .setComponent(new ComponentName("com.weather.information",
185                     "com.weather.information.activity.SpecificActivity"));
186             intent.putExtra("CHOSEN_DAY", (int) id);
187             OverviewFragment.this.getActivity().startActivity(intent);
188         } else {
189             // tablet layout
190             fragment.updateUIByChosenDay((int) id);
191         }
192     }
193
194     private interface UnitsConversor {
195         
196         public double doConversion(final double value);
197     }
198     
199     private void updateUI(final Forecast forecastWeatherData) {
200
201         final SharedPreferences sharedPreferences = PreferenceManager
202                 .getDefaultSharedPreferences(this.getActivity().getApplicationContext());
203
204         // TODO: repeating the same code in Overview, Specific and Current!!!
205         // 1. Update units of measurement.
206         String symbol;
207         UnitsConversor unitsConversor;
208         String keyPreference = this.getResources().getString(
209                 R.string.weather_preferences_temperature_key);
210         final String[] values = this.getResources().getStringArray(R.array.weather_preferences_temperature);
211         final String unitsPreferenceValue = sharedPreferences.getString(
212                 keyPreference, this.getString(R.string.weather_preferences_temperature_celsius));
213         if (unitsPreferenceValue.equals(values[0])) {
214                 symbol = values[0];
215                 unitsConversor = new UnitsConversor(){
216
217                                 @Override
218                                 public double doConversion(final double value) {
219                                         return value - 273.15;
220                                 }
221                         
222                 };
223         } else if (unitsPreferenceValue.equals(values[1])) {
224                 symbol = values[1];
225                 unitsConversor = new UnitsConversor(){
226
227                                 @Override
228                                 public double doConversion(final double value) {
229                                         return (value * 1.8) - 459.67;
230                                 }
231                         
232                 };
233         } else {
234                 symbol = values[2];
235                 unitsConversor = new UnitsConversor(){
236
237                                 @Override
238                                 public double doConversion(final double value) {
239                                         return value;
240                                 }
241                         
242                 };
243         }
244
245
246         // 2. Update number day forecast.
247         keyPreference = this.getResources().getString(R.string.weather_preferences_day_forecast_key);
248         final String dayForecast = sharedPreferences.getString(keyPreference, "5");
249         final int mDayForecast = Integer.valueOf(dayForecast);
250
251
252         // 3. Formatters
253         final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
254         tempFormatter.applyPattern("#####.##");
255         final SimpleDateFormat dayNameFormatter = new SimpleDateFormat("EEE", Locale.US);
256         final SimpleDateFormat monthAndDayNumberormatter = new SimpleDateFormat("MMM d", Locale.US);
257
258
259         // 4. Prepare data for UI.
260         final List<OverviewEntry> entries = new ArrayList<OverviewEntry>();
261         final OverviewAdapter adapter = new OverviewAdapter(this.getActivity(),
262                 R.layout.weather_main_entry_list);
263         final Calendar calendar = Calendar.getInstance();
264         int count = mDayForecast;
265         for (final com.weather.information.model.forecastweather.List forecast : forecastWeatherData
266                 .getList()) {
267
268             Bitmap picture;
269
270             if ((forecast.getWeather().size() > 0) &&
271                     (forecast.getWeather().get(0).getIcon() != null) &&
272                     (IconsList.getIcon(forecast.getWeather().get(0).getIcon()) != null)) {
273                 final String icon = forecast.getWeather().get(0).getIcon();
274                 picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
275                         .getResourceDrawable());
276             } else {
277                 picture = BitmapFactory.decodeResource(this.getResources(),
278                         R.drawable.weather_severe_alert);
279             }
280
281             final Long forecastUNIXDate = (Long) forecast.getDt();
282             calendar.setTimeInMillis(forecastUNIXDate * 1000L);
283             final Date dayTime = calendar.getTime();
284             final String dayTextName = dayNameFormatter.format(dayTime);
285             final String monthAndDayNumberText = monthAndDayNumberormatter.format(dayTime);
286
287             Double maxTemp = null;
288             if (forecast.getTemp().getMax() != null) {
289                 maxTemp = (Double) forecast.getTemp().getMax();
290                 maxTemp = unitsConversor.doConversion(maxTemp);
291             }
292
293             Double minTemp = null;
294             if (forecast.getTemp().getMin() != null) {
295                 minTemp = (Double) forecast.getTemp().getMin();
296                 minTemp = unitsConversor.doConversion(minTemp);
297             }
298
299             if ((maxTemp != null) && (minTemp != null)) {
300                 entries.add(new OverviewEntry(dayTextName, monthAndDayNumberText,
301                         tempFormatter.format(maxTemp) + symbol, tempFormatter.format(minTemp) + symbol,
302                         picture));
303             }
304
305             count = count - 1;
306             if (count == 0) {
307                 break;
308             }
309         }
310
311
312         // 5. Update UI.
313         adapter.addAll(entries);
314         this.setListAdapter(adapter);
315     }
316
317     private boolean isDataFresh(final Date lastUpdate) {
318         if (lastUpdate == null) {
319                 return false;
320         }
321         
322         final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
323                         this.getActivity().getApplicationContext());
324         final String keyPreference = this.getString(R.string.weather_preferences_refresh_interval_key);
325         final String refresh = sharedPreferences.getString(
326                         keyPreference,
327                         this.getResources().getStringArray(R.array.weather_preferences_refresh_interval)[0]);
328         final Date currentTime = new Date();
329         if (((currentTime.getTime() - lastUpdate.getTime())) < Long.valueOf(refresh)) {
330                 return true;
331         }
332         
333         return false;
334     }
335
336     private class OverviewTask extends AsyncTask<Object, Void, Forecast> {
337         // Store the context passed to the AsyncTask when the system instantiates it.
338         private final Context localContext;
339         private final CustomHTTPClient HTTPClient;
340         private final ServiceParser weatherService;
341
342         public OverviewTask(final Context context, final CustomHTTPClient HTTPClient,
343                         final ServiceParser weatherService) {
344                 this.localContext = context;
345             this.HTTPClient = HTTPClient;
346             this.weatherService = weatherService;
347         }
348         
349         @Override
350         protected Forecast doInBackground(final Object... params) {
351             final double latitude = (Double) params[0];
352             final double longitude = (Double) params[1];
353
354             Forecast forecast = null;
355
356             try {
357                 forecast = this.doInBackgroundThrowable(latitude, longitude);
358             } catch (final JsonParseException e) {
359                 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
360             } catch (final ClientProtocolException e) {
361                 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
362             } catch (final MalformedURLException e) {
363                 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
364             } catch (final URISyntaxException e) {
365                 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
366             } catch (final IOException e) {
367                 // logger infrastructure swallows UnknownHostException :/
368                 Log.e(TAG, "OverviewTask doInBackground exception: " + e.getMessage(), e);
369             } finally {
370                 HTTPClient.close();
371             }
372
373             return forecast;
374         }
375
376         private Forecast doInBackgroundThrowable(final double latitude, final double longitude)
377                         throws URISyntaxException, ClientProtocolException, JsonParseException, IOException {
378
379             final String APIVersion = localContext.getResources().getString(R.string.api_version);
380             final String urlAPI = localContext.getResources().getString(R.string.uri_api_weather_forecast);
381             // TODO: number as resource
382             final String url = weatherService.createURIAPIForecast(urlAPI, APIVersion, latitude, longitude, "14");
383             final String urlWithoutCache = url.concat("&time=" + System.currentTimeMillis());
384             final String jsonData = HTTPClient.retrieveDataAsString(new URL(urlWithoutCache));
385
386             return weatherService.retrieveForecastFromJPOS(jsonData);
387         }
388
389         @Override
390         protected void onPostExecute(final Forecast forecast) {
391                 
392             // Call updateUI on the UI thread.
393                 final Intent forecastData = new Intent("com.weather.information.UPDATEFORECAST");
394                 forecastData.putExtra("forecast", forecast);
395             LocalBroadcastManager.getInstance(this.localContext).sendBroadcastSync(forecastData);
396         }
397     }
398 }