1 package name.gumartinm.weather.information.fragment.overview;
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.ArrayList;
11 import java.util.Calendar;
12 import java.util.Date;
13 import java.util.List;
14 import java.util.Locale;
16 import org.apache.http.client.ClientProtocolException;
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;
36 import com.fasterxml.jackson.core.JsonParseException;
37 import name.gumartinm.weather.information.R;
38 import name.gumartinm.weather.information.fragment.specific.SpecificFragment;
39 import name.gumartinm.weather.information.httpclient.CustomHTTPClient;
40 import name.gumartinm.weather.information.model.DatabaseQueries;
41 import name.gumartinm.weather.information.model.WeatherLocation;
42 import name.gumartinm.weather.information.model.forecastweather.Forecast;
43 import name.gumartinm.weather.information.parser.JPOSForecastParser;
44 import name.gumartinm.weather.information.service.IconsList;
45 import name.gumartinm.weather.information.service.PermanentStorage;
46 import name.gumartinm.weather.information.service.ServiceForecastParser;
48 public class OverviewFragment extends ListFragment {
49 private static final String TAG = "OverviewFragment";
50 private BroadcastReceiver mReceiver;
53 public void onCreate(final Bundle savedInstanceState) {
54 super.onCreate(savedInstanceState);
58 public void onActivityCreated(final Bundle savedInstanceState) {
59 super.onActivityCreated(savedInstanceState);
61 final ListView listWeatherView = this.getListView();
62 listWeatherView.setChoiceMode(ListView.CHOICE_MODE_NONE);
64 if (savedInstanceState != null) {
66 final Forecast forecast = (Forecast) savedInstanceState.getSerializable("Forecast");
68 if (forecast != null) {
69 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
70 store.saveForecast(forecast);
74 this.setHasOptionsMenu(false);
76 this.setEmptyText(this.getString(R.string.text_field_remote_error));
77 this.setListShownNoAnimation(false);
81 public void onResume() {
84 this.mReceiver = new BroadcastReceiver() {
87 public void onReceive(final Context context, final Intent intent) {
88 final String action = intent.getAction();
89 if (action.equals("name.gumartinm.weather.information.UPDATEFORECAST")) {
90 final Forecast forecastRemote = (Forecast) intent.getSerializableExtra("forecast");
92 if (forecastRemote != null) {
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();
100 if (forecast == null || !OverviewFragment.this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
102 OverviewFragment.this.updateUI(forecastRemote);
105 store.saveForecast(forecastRemote);
106 weatherLocation.setLastForecastUIUpdate(new Date());
107 query.updateDataBase(weatherLocation);
110 OverviewFragment.this.setListShownNoAnimation(true);
114 // Empty list and show error message (see setEmptyText in onCreate)
115 OverviewFragment.this.setListAdapter(null);
116 OverviewFragment.this.setListShownNoAnimation(true);
123 final IntentFilter filter = new IntentFilter();
124 filter.addAction("name.gumartinm.weather.information.UPDATEFORECAST");
125 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext())
126 .registerReceiver(this.mReceiver, filter);
128 final DatabaseQueries query = new DatabaseQueries(this.getActivity().getApplicationContext());
129 final WeatherLocation weatherLocation = query.queryDataBase();
130 if (weatherLocation == null) {
132 // Empty list and show error message (see setEmptyText in onCreate)
133 this.setListAdapter(null);
134 this.setListShownNoAnimation(true);
138 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
139 final Forecast forecast = store.getForecast();
141 if (forecast != null && this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
142 this.updateUI(forecast);
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 ServiceForecastParser(new JPOSForecastParser()));
152 task.execute(weatherLocation.getLatitude(), weatherLocation.getLongitude());
157 public void onSaveInstanceState(final Bundle savedInstanceState) {
160 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
161 final Forecast forecast = store.getForecast();
163 if (forecast != null) {
164 savedInstanceState.putSerializable("Forecast", forecast);
167 super.onSaveInstanceState(savedInstanceState);
171 public void onPause() {
172 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(this.mReceiver);
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) {
183 final Intent intent = new Intent("name.gumartinm.weather.information.WEATHERINFO")
184 .setComponent(new ComponentName("name.gumartinm.weather.information",
185 "name.gumartinm.weather.information.activity.SpecificActivity"));
186 intent.putExtra("CHOSEN_DAY", (int) id);
187 OverviewFragment.this.getActivity().startActivity(intent);
190 fragment.updateUIByChosenDay((int) id);
194 private interface UnitsConversor {
196 public double doConversion(final double value);
199 private void updateUI(final Forecast forecastWeatherData) {
201 final SharedPreferences sharedPreferences = PreferenceManager
202 .getDefaultSharedPreferences(this.getActivity().getApplicationContext());
204 // TODO: repeating the same code in Overview, Specific and Current!!!
205 // 1. Update units of measurement.
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])) {
215 unitsConversor = new UnitsConversor(){
218 public double doConversion(final double value) {
219 return value - 273.15;
223 } else if (unitsPreferenceValue.equals(values[1])) {
225 unitsConversor = new UnitsConversor(){
228 public double doConversion(final double value) {
229 return (value * 1.8) - 459.67;
235 unitsConversor = new UnitsConversor(){
238 public double doConversion(final double value) {
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);
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);
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 name.gumartinm.weather.information.model.forecastweather.List forecast : forecastWeatherData
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());
277 picture = BitmapFactory.decodeResource(this.getResources(),
278 R.drawable.weather_severe_alert);
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);
287 Double maxTemp = null;
288 if (forecast.getTemp().getMax() != null) {
289 maxTemp = (Double) forecast.getTemp().getMax();
290 maxTemp = unitsConversor.doConversion(maxTemp);
293 Double minTemp = null;
294 if (forecast.getTemp().getMin() != null) {
295 minTemp = (Double) forecast.getTemp().getMin();
296 minTemp = unitsConversor.doConversion(minTemp);
299 if ((maxTemp != null) && (minTemp != null)) {
300 entries.add(new OverviewEntry(dayTextName, monthAndDayNumberText,
301 tempFormatter.format(maxTemp) + symbol, tempFormatter.format(minTemp) + symbol,
313 adapter.addAll(entries);
314 this.setListAdapter(adapter);
317 private boolean isDataFresh(final Date lastUpdate) {
318 if (lastUpdate == null) {
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(
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)) {
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 ServiceForecastParser weatherService;
342 public OverviewTask(final Context context, final CustomHTTPClient HTTPClient,
343 final ServiceForecastParser weatherService) {
344 this.localContext = context;
345 this.HTTPClient = HTTPClient;
346 this.weatherService = weatherService;
350 protected Forecast doInBackground(final Object... params) {
351 final double latitude = (Double) params[0];
352 final double longitude = (Double) params[1];
354 Forecast forecast = null;
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);
376 private Forecast doInBackgroundThrowable(final double latitude, final double longitude)
377 throws URISyntaxException, ClientProtocolException, JsonParseException, IOException {
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));
386 return weatherService.retrieveForecastFromJPOS(jsonData);
390 protected void onPostExecute(final Forecast forecast) {
392 // Call updateUI on the UI thread.
393 final Intent forecastData = new Intent("name.gumartinm.weather.information.UPDATEFORECAST");
394 forecastData.putExtra("forecast", forecast);
395 LocalBroadcastManager.getInstance(this.localContext).sendBroadcastSync(forecastData);