1 package uk
.me
.njae
.sunshine
;
3 import android
.content
.Intent
;
4 import android
.net
.Uri
;
5 import android
.os
.AsyncTask
;
6 import android
.os
.Bundle
;
7 import android
.support
.v4
.app
.Fragment
;
8 import android
.util
.Log
;
9 import android
.view
.LayoutInflater
;
10 import android
.view
.Menu
;
11 import android
.view
.MenuInflater
;
12 import android
.view
.MenuItem
;
13 import android
.view
.View
;
14 import android
.view
.ViewGroup
;
15 import android
.widget
.AdapterView
;
16 import android
.widget
.ArrayAdapter
;
17 import android
.widget
.ListView
;
18 import android
.widget
.Toast
;
20 import org
.json
.JSONArray
;
21 import org
.json
.JSONException
;
22 import org
.json
.JSONObject
;
24 import java
.io
.BufferedReader
;
25 import java
.io
.IOException
;
26 import java
.io
.InputStream
;
27 import java
.io
.InputStreamReader
;
28 import java
.net
.HttpURLConnection
;
30 import java
.text
.SimpleDateFormat
;
31 import java
.util
.ArrayList
;
32 import java
.util
.Arrays
;
33 import java
.util
.Date
;
34 import java
.util
.List
;
37 * A placeholder fragment containing a simple view.
39 public class ForecastFragment
extends Fragment
{
41 private ArrayAdapter
<String
> mForecastAdapter
;
43 public ForecastFragment() {
47 public void onCreate(Bundle savedInstanceState
) {
48 super.onCreate(savedInstanceState
);
49 setHasOptionsMenu(true);
53 public void onCreateOptionsMenu(Menu menu
, MenuInflater inflater
) {
54 // Inflate the menu; this adds items to the action bar if it is present.
55 inflater
.inflate(R
.menu
.forecastfragment
, menu
);
59 public boolean onOptionsItemSelected(MenuItem item
) {
60 // Handle action bar item clicks here. The action bar will
61 // automatically handle clicks on the Home/Up button, so long
62 // as you specify a parent activity in AndroidManifest.xml.
63 int id
= item
.getItemId();
64 if (id
== R
.id
.action_refresh
) {
65 FetchWeatherTask weatherTask
= new FetchWeatherTask();
66 weatherTask
.execute("2642465");
69 return super.onOptionsItemSelected(item
);
73 public View
onCreateView(LayoutInflater inflater
, ViewGroup container
,
74 Bundle savedInstanceState
) {
76 String
[] forecastArray
= {
77 "Today - Sunny - 10/10",
78 "Tomorrow - Cloudy - 11/11",
79 "Tuesday - Snow - 12/12",
80 "Wednesday - Rain - 13/13",
81 "Thursday - Hail - 14/14",
82 "Friday - Scorchio - 15/15",
83 "Saturday - Fog - 16/16"
86 List
<String
> weekForecast
= new ArrayList
<String
>(
87 Arrays
.asList(forecastArray
));
88 mForecastAdapter
= new ArrayAdapter
<String
>(
90 R
.layout
.list_item_forecast
,
91 R
.id
.list_item_forecast_textview
,
95 View rootView
= inflater
.inflate(R
.layout
.fragment_main
, container
, false);
97 ListView listView
= (ListView
) rootView
.findViewById(R
.id
.listview_forecast
);
98 listView
.setAdapter(mForecastAdapter
);
99 listView
.setOnItemClickListener(new AdapterView
.OnItemClickListener() {
101 public void onItemClick(AdapterView
<?
> adapterView
, View view
, int position
, long l
) {
102 String forecast
= mForecastAdapter
.getItem(position
);
103 Intent intent
= new Intent(getActivity(), DetailActivity
.class)
104 .putExtra(Intent
.EXTRA_TEXT
, forecast
);
105 startActivity(intent
);
112 public class FetchWeatherTask
extends AsyncTask
<String
, Void
, String
[]> {
114 private final String LOG_TAG
= FetchWeatherTask
.class.getSimpleName();
117 protected void onPostExecute(String
[] result
) {
118 if (result
!= null) {
119 mForecastAdapter
.clear();
120 // for (String dayForecastStr: result) {
121 // mForecastAdapter.add(dayForecastStr);
123 mForecastAdapter
.addAll(result
);
128 protected String
[] doInBackground(String
... params
) {
129 if (params
.length
== 0) {
133 // These two need to be declared outside the try/catch
134 // so that they can be closed in the finally block.
135 HttpURLConnection urlConnection
= null;
136 BufferedReader reader
= null;
138 // Will contain the raw JSON response as a string.
139 String forecastJsonStr
;
141 String format
= "json";
142 String units
= "metric";
146 // Construct the URL for the OpenWeatherMap query
147 // Possible parameters are avaiable at OWM's forecast API page, at
148 // http://openweathermap.org/API#forecast
149 final String FORECAST_BASE_URL
= "http://api.openweathermap.org/data/2.5/forecast/daily?";
150 final String QUERY_PARAM
= "id";
151 final String FORMAT_PARAM
= "mode";
152 final String UNITS_PARAM
= "units";
153 final String DAYS_PARAM
= "cnt";
155 Uri builtUri
= Uri
.parse(FORECAST_BASE_URL
).buildUpon()
156 .appendQueryParameter(QUERY_PARAM
, params
[0])
157 .appendQueryParameter(FORMAT_PARAM
, format
)
158 .appendQueryParameter(UNITS_PARAM
, units
)
159 .appendQueryParameter(DAYS_PARAM
, Integer
.toString(numDays
))
162 URL url
= new URL(builtUri
.toString());
163 /**/ Log
.v(LOG_TAG
, "Built URI " + builtUri
.toString());
165 // Create the request to OpenWeatherMap, and open the connection
166 urlConnection
= (HttpURLConnection
) url
.openConnection();
167 urlConnection
.setRequestMethod("GET");
168 urlConnection
.connect();
170 // Read the input stream into a String
171 InputStream inputStream
= urlConnection
.getInputStream();
172 StringBuffer buffer
= new StringBuffer();
173 if (inputStream
== null) {
177 reader
= new BufferedReader(new InputStreamReader(inputStream
));
180 while ((line
= reader
.readLine()) != null) {
181 // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
182 // But it does make debugging a *lot* easier if you print out the completed
183 // buffer for debugging.
184 buffer
.append(line
+ "\n");
187 if (buffer
.length() == 0) {
188 // Stream was empty. No point in parsing.
191 forecastJsonStr
= buffer
.toString();
192 // Log.v(LOG_TAG, "Forecast JSON string: " + forecastJsonStr);
194 return getWeatherDataFromJson(forecastJsonStr
, numDays
);
195 } catch (JSONException e
) {
196 Log
.e(LOG_TAG
, e
.getMessage(), e
);
200 // This will only happen if we fail to read the forecast
203 } catch (IOException e
) {
204 Log
.e(LOG_TAG
, "Error ", e
);
205 // If the code didn't successfully get the weather data, there's no point in attempting
207 forecastJsonStr
= null;
209 if (urlConnection
!= null) {
210 urlConnection
.disconnect();
212 if (reader
!= null) {
215 } catch (final IOException e
) {
216 Log
.e(LOG_TAG
, "Error closing stream", e
);
223 /* The date/time conversion code is going to be moved outside the asynctask later,
224 * so for convenience we're breaking it out into its own method now.
226 private String
getReadableDateString(long time
) {
227 // Because the API returns a unix timestamp (measured in seconds),
228 // it must be converted to milliseconds in order to be converted to valid date.
229 Date date
= new Date(time
* 1000);
230 SimpleDateFormat format
= new SimpleDateFormat("E, MMM d");
231 return format
.format(date
).toString();
235 * Prepare the weather high/lows for presentation.
237 private String
formatHighLows(double high
, double low
) {
238 // For presentation, assume the user doesn't care about tenths of a degree.
239 long roundedHigh
= Math
.round(high
);
240 long roundedLow
= Math
.round(low
);
242 String highLowStr
= roundedHigh
+ "/" + roundedLow
;
247 * Take the String representing the complete forecast in JSON Format and
248 * pull out the data we need to construct the Strings needed for the wireframes.
250 * Fortunately parsing is easy: constructor takes the JSON string and converts it
251 * into an Object hierarchy for us.
253 private String
[] getWeatherDataFromJson(String forecastJsonStr
, int numDays
)
254 throws JSONException
{
256 // These are the names of the JSON objects that need to be extracted.
257 final String OWM_LIST
= "list";
258 final String OWM_WEATHER
= "weather";
259 final String OWM_TEMPERATURE
= "temp";
260 final String OWM_MAX
= "max";
261 final String OWM_MIN
= "min";
262 final String OWM_DATETIME
= "dt";
263 final String OWM_DESCRIPTION
= "main";
265 JSONObject forecastJson
= new JSONObject(forecastJsonStr
);
266 JSONArray weatherArray
= forecastJson
.getJSONArray(OWM_LIST
);
268 String
[] resultStrs
= new String
[numDays
];
269 for (int i
= 0; i
< weatherArray
.length(); i
++) {
270 // For now, using the format "Day, description, hi/low"
275 // Get the JSON object representing the day
276 JSONObject dayForecast
= weatherArray
.getJSONObject(i
);
278 // The date/time is returned as a long. We need to convert that
279 // into something human-readable, since most people won't read "1400356800" as
281 long dateTime
= dayForecast
.getLong(OWM_DATETIME
);
282 day
= getReadableDateString(dateTime
);
284 // description is in a child array called "weather", which is 1 element long.
285 JSONObject weatherObject
= dayForecast
.getJSONArray(OWM_WEATHER
).getJSONObject(0);
286 description
= weatherObject
.getString(OWM_DESCRIPTION
);
288 // Temperatures are in a child object called "temp". Try not to name variables
289 // "temp" when working with temperature. It confuses everybody.
290 JSONObject temperatureObject
= dayForecast
.getJSONObject(OWM_TEMPERATURE
);
291 double high
= temperatureObject
.getDouble(OWM_MAX
);
292 double low
= temperatureObject
.getDouble(OWM_MIN
);
294 highAndLow
= formatHighLows(high
, low
);
295 resultStrs
[i
] = day
+ " - " + description
+ " - " + highAndLow
;
298 // for (String s: resultStrs) {
299 // Log.v(LOG_TAG, "Forecast entry: " + s);