1 package uk
.me
.njae
.sunshine
;
3 import android
.net
.Uri
;
4 import android
.os
.AsyncTask
;
5 import android
.os
.Bundle
;
6 import android
.support
.v4
.app
.Fragment
;
7 import android
.util
.Log
;
8 import android
.view
.LayoutInflater
;
9 import android
.view
.Menu
;
10 import android
.view
.MenuInflater
;
11 import android
.view
.MenuItem
;
12 import android
.view
.View
;
13 import android
.view
.ViewGroup
;
14 import android
.widget
.ArrayAdapter
;
15 import android
.widget
.ListView
;
17 import org
.json
.JSONArray
;
18 import org
.json
.JSONException
;
19 import org
.json
.JSONObject
;
21 import java
.io
.BufferedReader
;
22 import java
.io
.IOException
;
23 import java
.io
.InputStream
;
24 import java
.io
.InputStreamReader
;
25 import java
.net
.HttpURLConnection
;
27 import java
.text
.SimpleDateFormat
;
28 import java
.util
.ArrayList
;
29 import java
.util
.Arrays
;
30 import java
.util
.Date
;
31 import java
.util
.List
;
34 * A placeholder fragment containing a simple view.
36 public class ForecastFragment
extends Fragment
{
38 private ArrayAdapter
<String
> mForecastAdapter
;
40 public ForecastFragment() {
44 public void onCreate(Bundle savedInstanceState
) {
45 super.onCreate(savedInstanceState
);
46 setHasOptionsMenu(true);
50 public void onCreateOptionsMenu(Menu menu
, MenuInflater inflater
) {
51 // Inflate the menu; this adds items to the action bar if it is present.
52 inflater
.inflate(R
.menu
.forecastfragment
, menu
);
56 public boolean onOptionsItemSelected(MenuItem item
) {
57 // Handle action bar item clicks here. The action bar will
58 // automatically handle clicks on the Home/Up button, so long
59 // as you specify a parent activity in AndroidManifest.xml.
60 int id
= item
.getItemId();
61 if (id
== R
.id
.action_refresh
) {
62 FetchWeatherTask weatherTask
= new FetchWeatherTask();
63 weatherTask
.execute("94043");
66 return super.onOptionsItemSelected(item
);
70 public View
onCreateView(LayoutInflater inflater
, ViewGroup container
,
71 Bundle savedInstanceState
) {
73 String
[] forecastArray
= {
74 "Today - Sunny - 10/10",
75 "Tomorrow - Cloudy - 11/11",
76 "Tuesday - Snow - 12/12",
77 "Wednesday - Rain - 13/13",
78 "Thursday - Hail - 14/14",
79 "Friday - Scorchio - 15/15",
80 "Saturday - Fog - 16/16"
83 List
<String
> weekForecast
= new ArrayList
<String
>(
84 Arrays
.asList(forecastArray
));
85 mForecastAdapter
= new ArrayAdapter
<String
>(
87 R
.layout
.list_item_forecast
,
88 R
.id
.list_item_forecast_textview
,
92 View rootView
= inflater
.inflate(R
.layout
.fragment_main
, container
, false);
94 ListView list_view
= (ListView
) rootView
.findViewById(R
.id
.listview_forecast
);
95 list_view
.setAdapter(mForecastAdapter
);
100 public class FetchWeatherTask
extends AsyncTask
<String
, Void
, String
[]> {
102 private final String LOG_TAG
= FetchWeatherTask
.class.getSimpleName();
105 protected void onPostExecute(String
[] result
) {
106 if (result
!= null) {
107 mForecastAdapter
.clear();
108 // for (String dayForecastStr: result) {
109 // mForecastAdapter.add(dayForecastStr);
111 mForecastAdapter
.addAll(result
);
116 protected String
[] doInBackground(String
... params
) {
117 if (params
.length
== 0) {
121 // These two need to be declared outside the try/catch
122 // so that they can be closed in the finally block.
123 HttpURLConnection urlConnection
= null;
124 BufferedReader reader
= null;
126 // Will contain the raw JSON response as a string.
127 String forecastJsonStr
;
129 String format
= "json";
130 String units
= "metric";
134 // Construct the URL for the OpenWeatherMap query
135 // Possible parameters are avaiable at OWM's forecast API page, at
136 // http://openweathermap.org/API#forecast
137 final String FORECAST_BASE_URL
= "http://api.openweathermap.org/data/2.5/forecast/daily?";
138 final String QUERY_PARAM
= "q";
139 final String FORMAT_PARAM
= "mode";
140 final String UNITS_PARAM
= "units";
141 final String DAYS_PARAM
= "cnt";
143 Uri builtUri
= Uri
.parse(FORECAST_BASE_URL
).buildUpon()
144 .appendQueryParameter(QUERY_PARAM
, params
[0])
145 .appendQueryParameter(FORMAT_PARAM
, format
)
146 .appendQueryParameter(UNITS_PARAM
, units
)
147 .appendQueryParameter(DAYS_PARAM
, Integer
.toString(numDays
))
150 URL url
= new URL(builtUri
.toString());
151 /**/ Log
.v(LOG_TAG
, "Built URI " + builtUri
.toString());
153 // Create the request to OpenWeatherMap, and open the connection
154 urlConnection
= (HttpURLConnection
) url
.openConnection();
155 urlConnection
.setRequestMethod("GET");
156 urlConnection
.connect();
158 // Read the input stream into a String
159 InputStream inputStream
= urlConnection
.getInputStream();
160 StringBuffer buffer
= new StringBuffer();
161 if (inputStream
== null) {
165 reader
= new BufferedReader(new InputStreamReader(inputStream
));
168 while ((line
= reader
.readLine()) != null) {
169 // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
170 // But it does make debugging a *lot* easier if you print out the completed
171 // buffer for debugging.
172 buffer
.append(line
+ "\n");
175 if (buffer
.length() == 0) {
176 // Stream was empty. No point in parsing.
179 forecastJsonStr
= buffer
.toString();
180 // Log.v(LOG_TAG, "Forecast JSON string: " + forecastJsonStr);
182 return getWeatherDataFromJson(forecastJsonStr
, numDays
);
183 } catch (JSONException e
) {
184 Log
.e(LOG_TAG
, e
.getMessage(), e
);
188 // This will only happen if we fail to read the forecast
191 } catch (IOException e
) {
192 Log
.e(LOG_TAG
, "Error ", e
);
193 // If the code didn't successfully get the weather data, there's no point in attempting
195 forecastJsonStr
= null;
197 if (urlConnection
!= null) {
198 urlConnection
.disconnect();
200 if (reader
!= null) {
203 } catch (final IOException e
) {
204 Log
.e(LOG_TAG
, "Error closing stream", e
);
211 /* The date/time conversion code is going to be moved outside the asynctask later,
212 * so for convenience we're breaking it out into its own method now.
214 private String
getReadableDateString(long time
) {
215 // Because the API returns a unix timestamp (measured in seconds),
216 // it must be converted to milliseconds in order to be converted to valid date.
217 Date date
= new Date(time
* 1000);
218 SimpleDateFormat format
= new SimpleDateFormat("E, MMM d");
219 return format
.format(date
).toString();
223 * Prepare the weather high/lows for presentation.
225 private String
formatHighLows(double high
, double low
) {
226 // For presentation, assume the user doesn't care about tenths of a degree.
227 long roundedHigh
= Math
.round(high
);
228 long roundedLow
= Math
.round(low
);
230 String highLowStr
= roundedHigh
+ "/" + roundedLow
;
235 * Take the String representing the complete forecast in JSON Format and
236 * pull out the data we need to construct the Strings needed for the wireframes.
238 * Fortunately parsing is easy: constructor takes the JSON string and converts it
239 * into an Object hierarchy for us.
241 private String
[] getWeatherDataFromJson(String forecastJsonStr
, int numDays
)
242 throws JSONException
{
244 // These are the names of the JSON objects that need to be extracted.
245 final String OWM_LIST
= "list";
246 final String OWM_WEATHER
= "weather";
247 final String OWM_TEMPERATURE
= "temp";
248 final String OWM_MAX
= "max";
249 final String OWM_MIN
= "min";
250 final String OWM_DATETIME
= "dt";
251 final String OWM_DESCRIPTION
= "main";
253 JSONObject forecastJson
= new JSONObject(forecastJsonStr
);
254 JSONArray weatherArray
= forecastJson
.getJSONArray(OWM_LIST
);
256 String
[] resultStrs
= new String
[numDays
];
257 for (int i
= 0; i
< weatherArray
.length(); i
++) {
258 // For now, using the format "Day, description, hi/low"
263 // Get the JSON object representing the day
264 JSONObject dayForecast
= weatherArray
.getJSONObject(i
);
266 // The date/time is returned as a long. We need to convert that
267 // into something human-readable, since most people won't read "1400356800" as
269 long dateTime
= dayForecast
.getLong(OWM_DATETIME
);
270 day
= getReadableDateString(dateTime
);
272 // description is in a child array called "weather", which is 1 element long.
273 JSONObject weatherObject
= dayForecast
.getJSONArray(OWM_WEATHER
).getJSONObject(0);
274 description
= weatherObject
.getString(OWM_DESCRIPTION
);
276 // Temperatures are in a child object called "temp". Try not to name variables
277 // "temp" when working with temperature. It confuses everybody.
278 JSONObject temperatureObject
= dayForecast
.getJSONObject(OWM_TEMPERATURE
);
279 double high
= temperatureObject
.getDouble(OWM_MAX
);
280 double low
= temperatureObject
.getDouble(OWM_MIN
);
282 highAndLow
= formatHighLows(high
, low
);
283 resultStrs
[i
] = day
+ " - " + description
+ " - " + highAndLow
;
286 // for (String s: resultStrs) {
287 // Log.v(LOG_TAG, "Forecast entry: " + s);