2 * Copyright 2014 Gustavo Martin Morcuende
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package name.gumartinm.weather.information.fragment.overview;
18 import java.io.IOException;
19 import java.net.MalformedURLException;
20 import java.net.URISyntaxException;
22 import java.text.DecimalFormat;
23 import java.text.NumberFormat;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.List;
29 import java.util.Locale;
31 import org.apache.http.client.ClientProtocolException;
33 import android.content.BroadcastReceiver;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.content.SharedPreferences;
39 import android.graphics.Bitmap;
40 import android.graphics.BitmapFactory;
41 import android.net.http.AndroidHttpClient;
42 import android.os.AsyncTask;
43 import android.os.Bundle;
44 import android.preference.PreferenceManager;
45 import android.support.v4.app.ListFragment;
46 import android.support.v4.content.LocalBroadcastManager;
47 import android.util.Log;
48 import android.view.View;
49 import android.widget.ListView;
51 import com.fasterxml.jackson.core.JsonParseException;
52 import name.gumartinm.weather.information.R;
53 import name.gumartinm.weather.information.fragment.specific.SpecificFragment;
54 import name.gumartinm.weather.information.httpclient.CustomHTTPClient;
55 import name.gumartinm.weather.information.model.DatabaseQueries;
56 import name.gumartinm.weather.information.model.WeatherLocation;
57 import name.gumartinm.weather.information.model.forecastweather.Forecast;
58 import name.gumartinm.weather.information.parser.JPOSForecastParser;
59 import name.gumartinm.weather.information.service.IconsList;
60 import name.gumartinm.weather.information.service.PermanentStorage;
61 import name.gumartinm.weather.information.service.ServiceForecastParser;
63 public class OverviewFragment extends ListFragment {
64 private static final String TAG = "OverviewFragment";
65 private BroadcastReceiver mReceiver;
68 public void onCreate(final Bundle savedInstanceState) {
69 super.onCreate(savedInstanceState);
73 public void onActivityCreated(final Bundle savedInstanceState) {
74 super.onActivityCreated(savedInstanceState);
76 final ListView listWeatherView = this.getListView();
77 listWeatherView.setChoiceMode(ListView.CHOICE_MODE_NONE);
79 if (savedInstanceState != null) {
81 final Forecast forecast = (Forecast) savedInstanceState.getSerializable("Forecast");
83 if (forecast != null) {
84 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
85 store.saveForecast(forecast);
89 this.setHasOptionsMenu(false);
91 this.setEmptyText(this.getString(R.string.text_field_remote_error));
92 this.setListShownNoAnimation(false);
96 public void onResume() {
99 this.mReceiver = new BroadcastReceiver() {
102 public void onReceive(final Context context, final Intent intent) {
103 final String action = intent.getAction();
104 if (action.equals("name.gumartinm.weather.information.UPDATEFORECAST")) {
105 final Forecast forecastRemote = (Forecast) intent.getSerializableExtra("forecast");
107 if (forecastRemote != null) {
109 // 1. Check conditions. They must be the same as the ones that triggered the AsyncTask.
110 final DatabaseQueries query = new DatabaseQueries(context.getApplicationContext());
111 final WeatherLocation weatherLocation = query.queryDataBase();
112 final PermanentStorage store = new PermanentStorage(context.getApplicationContext());
113 final Forecast forecast = store.getForecast();
115 if (forecast == null || !OverviewFragment.this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
117 OverviewFragment.this.updateUI(forecastRemote);
120 store.saveForecast(forecastRemote);
121 weatherLocation.setLastForecastUIUpdate(new Date());
122 query.updateDataBase(weatherLocation);
125 OverviewFragment.this.setListShownNoAnimation(true);
129 // Empty list and show error message (see setEmptyText in onCreate)
130 OverviewFragment.this.setListAdapter(null);
131 OverviewFragment.this.setListShownNoAnimation(true);
138 final IntentFilter filter = new IntentFilter();
139 filter.addAction("name.gumartinm.weather.information.UPDATEFORECAST");
140 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext())
141 .registerReceiver(this.mReceiver, filter);
143 final DatabaseQueries query = new DatabaseQueries(this.getActivity().getApplicationContext());
144 final WeatherLocation weatherLocation = query.queryDataBase();
145 if (weatherLocation == null) {
147 // Empty list and show error message (see setEmptyText in onCreate)
148 this.setListAdapter(null);
149 this.setListShownNoAnimation(true);
153 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
154 final Forecast forecast = store.getForecast();
156 if (forecast != null && this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
157 this.updateUI(forecast);
159 // Load remote data (aynchronous)
160 // Gets the data from the web.
161 this.setListShownNoAnimation(false);
162 final OverviewTask task = new OverviewTask(
163 this.getActivity().getApplicationContext(),
164 new CustomHTTPClient(AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent")),
165 new ServiceForecastParser(new JPOSForecastParser()));
167 task.execute(weatherLocation.getLatitude(), weatherLocation.getLongitude());
172 public void onSaveInstanceState(final Bundle savedInstanceState) {
175 final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
176 final Forecast forecast = store.getForecast();
178 if (forecast != null) {
179 savedInstanceState.putSerializable("Forecast", forecast);
182 super.onSaveInstanceState(savedInstanceState);
186 public void onPause() {
187 LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(this.mReceiver);
193 public void onListItemClick(final ListView l, final View v, final int position, final long id) {
194 final SpecificFragment fragment = (SpecificFragment) this
195 .getFragmentManager().findFragmentById(R.id.weather_specific_fragment);
196 if (fragment == null) {
198 final Intent intent = new Intent("name.gumartinm.weather.information.WEATHERINFO")
199 .setComponent(new ComponentName("name.gumartinm.weather.information",
200 "name.gumartinm.weather.information.activity.SpecificActivity"));
201 intent.putExtra("CHOSEN_DAY", (int) id);
202 OverviewFragment.this.getActivity().startActivity(intent);
205 fragment.updateUIByChosenDay((int) id);
209 private interface UnitsConversor {
211 public double doConversion(final double value);
214 private void updateUI(final Forecast forecastWeatherData) {
216 final SharedPreferences sharedPreferences = PreferenceManager
217 .getDefaultSharedPreferences(this.getActivity().getApplicationContext());
219 // TODO: repeating the same code in Overview, Specific and Current!!!
220 // 1. Update units of measurement.
222 UnitsConversor unitsConversor;
223 String keyPreference = this.getResources().getString(
224 R.string.weather_preferences_temperature_key);
225 final String[] values = this.getResources().getStringArray(R.array.weather_preferences_temperature);
226 final String unitsPreferenceValue = sharedPreferences.getString(
227 keyPreference, this.getString(R.string.weather_preferences_temperature_celsius));
228 if (unitsPreferenceValue.equals(values[0])) {
230 unitsConversor = new UnitsConversor(){
233 public double doConversion(final double value) {
234 return value - 273.15;
238 } else if (unitsPreferenceValue.equals(values[1])) {
240 unitsConversor = new UnitsConversor(){
243 public double doConversion(final double value) {
244 return (value * 1.8) - 459.67;
250 unitsConversor = new UnitsConversor(){
253 public double doConversion(final double value) {
261 // 2. Update number day forecast.
262 keyPreference = this.getResources().getString(R.string.weather_preferences_day_forecast_key);
263 final String dayForecast = sharedPreferences.getString(keyPreference, "5");
264 final int mDayForecast = Integer.valueOf(dayForecast);
268 final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
269 tempFormatter.applyPattern("#####.##");
270 final SimpleDateFormat dayNameFormatter = new SimpleDateFormat("EEE", Locale.US);
271 final SimpleDateFormat monthAndDayNumberormatter = new SimpleDateFormat("MMM d", Locale.US);
274 // 4. Prepare data for UI.
275 final List<OverviewEntry> entries = new ArrayList<OverviewEntry>();
276 final OverviewAdapter adapter = new OverviewAdapter(this.getActivity(),
277 R.layout.weather_main_entry_list);
278 final Calendar calendar = Calendar.getInstance();
279 int count = mDayForecast;
280 for (final name.gumartinm.weather.information.model.forecastweather.List forecast : forecastWeatherData
285 if ((forecast.getWeather().size() > 0) &&
286 (forecast.getWeather().get(0).getIcon() != null) &&
287 (IconsList.getIcon(forecast.getWeather().get(0).getIcon()) != null)) {
288 final String icon = forecast.getWeather().get(0).getIcon();
289 picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
290 .getResourceDrawable());
292 picture = BitmapFactory.decodeResource(this.getResources(),
293 R.drawable.weather_severe_alert);
296 final Long forecastUNIXDate = (Long) forecast.getDt();
297 calendar.setTimeInMillis(forecastUNIXDate * 1000L);
298 final Date dayTime = calendar.getTime();
299 final String dayTextName = dayNameFormatter.format(dayTime);
300 final String monthAndDayNumberText = monthAndDayNumberormatter.format(dayTime);
302 Double maxTemp = null;
303 if (forecast.getTemp().getMax() != null) {
304 maxTemp = (Double) forecast.getTemp().getMax();
305 maxTemp = unitsConversor.doConversion(maxTemp);
308 Double minTemp = null;
309 if (forecast.getTemp().getMin() != null) {
310 minTemp = (Double) forecast.getTemp().getMin();
311 minTemp = unitsConversor.doConversion(minTemp);
314 if ((maxTemp != null) && (minTemp != null)) {
315 entries.add(new OverviewEntry(dayTextName, monthAndDayNumberText,
316 tempFormatter.format(maxTemp) + symbol, tempFormatter.format(minTemp) + symbol,
328 adapter.addAll(entries);
329 this.setListAdapter(adapter);
332 private boolean isDataFresh(final Date lastUpdate) {
333 if (lastUpdate == null) {
337 final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(
338 this.getActivity().getApplicationContext());
339 final String keyPreference = this.getString(R.string.weather_preferences_refresh_interval_key);
340 final String refresh = sharedPreferences.getString(
342 this.getResources().getStringArray(R.array.weather_preferences_refresh_interval)[0]);
343 final Date currentTime = new Date();
344 if (((currentTime.getTime() - lastUpdate.getTime())) < Long.valueOf(refresh)) {
351 private class OverviewTask extends AsyncTask<Object, Void, Forecast> {
352 // Store the context passed to the AsyncTask when the system instantiates it.
353 private final Context localContext;
354 private final CustomHTTPClient HTTPClient;
355 private final ServiceForecastParser weatherService;
357 public OverviewTask(final Context context, final CustomHTTPClient HTTPClient,
358 final ServiceForecastParser weatherService) {
359 this.localContext = context;
360 this.HTTPClient = HTTPClient;
361 this.weatherService = weatherService;
365 protected Forecast doInBackground(final Object... params) {
366 final double latitude = (Double) params[0];
367 final double longitude = (Double) params[1];
369 Forecast forecast = null;
372 forecast = this.doInBackgroundThrowable(latitude, longitude);
373 } catch (final JsonParseException e) {
374 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
375 } catch (final ClientProtocolException e) {
376 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
377 } catch (final MalformedURLException e) {
378 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
379 } catch (final URISyntaxException e) {
380 Log.e(TAG, "OverviewTask doInBackground exception: ", e);
381 } catch (final IOException e) {
382 // logger infrastructure swallows UnknownHostException :/
383 Log.e(TAG, "OverviewTask doInBackground exception: " + e.getMessage(), e);
391 private Forecast doInBackgroundThrowable(final double latitude, final double longitude)
392 throws URISyntaxException, ClientProtocolException, JsonParseException, IOException {
394 final String APIVersion = localContext.getResources().getString(R.string.api_version);
395 final String urlAPI = localContext.getResources().getString(R.string.uri_api_weather_forecast);
396 // TODO: number as resource
397 final String url = weatherService.createURIAPIForecast(urlAPI, APIVersion, latitude, longitude, "14");
398 final String urlWithoutCache = url.concat("&time=" + System.currentTimeMillis());
399 final String jsonData = HTTPClient.retrieveDataAsString(new URL(urlWithoutCache));
401 return weatherService.retrieveForecastFromJPOS(jsonData);
405 protected void onPostExecute(final Forecast forecast) {
407 // Call updateUI on the UI thread.
408 final Intent forecastData = new Intent("name.gumartinm.weather.information.UPDATEFORECAST");
409 forecastData.putExtra("forecast", forecast);
410 LocalBroadcastManager.getInstance(this.localContext).sendBroadcastSync(forecastData);