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