Finished lesson 4
[Sunshine.git] / app / src / main / java / uk / me / njae / sunshine / ForecastFragment.java
1 package uk.me.njae.sunshine;
2
3 import android.content.Intent;
4 import android.content.SharedPreferences;
5 import android.database.Cursor;
6 import android.net.Uri;
7 import android.os.Bundle;
8 import android.preference.PreferenceManager;
9 import android.support.v4.app.Fragment;
10 import android.support.v4.app.LoaderManager;
11 import android.support.v4.content.CursorLoader;
12 import android.support.v4.content.Loader;
13 import android.support.v4.widget.SimpleCursorAdapter;
14 import android.view.LayoutInflater;
15 import android.view.Menu;
16 import android.view.MenuInflater;
17 import android.view.MenuItem;
18 import android.view.View;
19 import android.view.ViewGroup;
20 import android.widget.AdapterView;
21 import android.widget.ListView;
22 import android.widget.TextView;
23
24 import java.io.UnsupportedEncodingException;
25 import java.net.URLEncoder;
26 import java.util.Date;
27
28 import uk.me.njae.sunshine.data.WeatherContract;
29 import uk.me.njae.sunshine.data.WeatherContract.LocationEntry;
30 import uk.me.njae.sunshine.data.WeatherContract.WeatherEntry;
31
32 import static android.util.Log.e;
33
34 /**
35 * A placeholder fragment containing a simple view.
36 */
37 public class ForecastFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
38
39 private final String LOG_TAG = ForecastFragment.class.getSimpleName();
40
41 private SimpleCursorAdapter mForecastAdapter;
42
43 private static final int FORECAST_LOADER = 0;
44 private String mLocation;
45
46 // For the forecast view we're showing only a small subset of the stored data.
47 // Specify the columns we need.
48 private static final String[] FORECAST_COLUMNS = {
49 // In this case the id needs to be fully qualified with a table name, since
50 // the content provider joins the location & weather tables in the background
51 // (both have an _id column)
52 // On the one hand, that's annoying. On the other, you can search the weather table
53 // using the location set by the user, which is only in the Location table.
54 // So the convenience is worth it.
55 WeatherEntry.TABLE_NAME + "." + WeatherEntry._ID,
56 WeatherEntry.COLUMN_DATETEXT,
57 WeatherEntry.COLUMN_SHORT_DESC,
58 WeatherEntry.COLUMN_MAX_TEMP,
59 WeatherEntry.COLUMN_MIN_TEMP,
60 LocationEntry.COLUMN_LOCATION_SETTING
61 };
62
63 // These indices are tied to FORECAST_COLUMNS. If FORECAST_COLUMNS changes, these
64 // must change.
65 public static final int COL_WEATHER_ID = 0;
66 public static final int COL_WEATHER_DATE = 1;
67 public static final int COL_WEATHER_DESC = 2;
68 public static final int COL_WEATHER_MAX_TEMP = 3;
69 public static final int COL_WEATHER_MIN_TEMP = 4;
70 public static final int COL_LOCATION_SETTING = 5;
71
72 public ForecastFragment() {
73 }
74
75 @Override
76 public void onCreate(Bundle savedInstanceState) {
77 super.onCreate(savedInstanceState);
78 setHasOptionsMenu(true);
79 }
80
81 @Override
82 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
83 // Inflate the menu; this adds items to the action bar if it is present.
84 inflater.inflate(R.menu.forecastfragment, menu);
85 }
86
87 @Override
88 public boolean onOptionsItemSelected(MenuItem item) {
89 // Handle action bar item clicks here. The action bar will
90 // automatically handle clicks on the Home/Up button, so long
91 // as you specify a parent activity in AndroidManifest.xml.
92 int id = item.getItemId();
93 if (id == R.id.action_refresh) {
94 updateWeather();
95 return true;
96 }
97 if (id == R.id.action_show_location) {
98 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
99 String location = preferences.getString(getString(R.string.pref_location_key),
100 getString(R.string.pref_location_default));
101 Intent intent = new Intent(Intent.ACTION_VIEW);
102 Uri geoLocation;
103 try {
104 geoLocation = Uri.parse("geo:0,0?q=" + URLEncoder.encode(location, "UTF-8"));
105 intent.setData(geoLocation);
106 } catch (UnsupportedEncodingException e) {
107 e(LOG_TAG, "Error ", e);
108 }
109 if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
110 startActivity(intent);
111 }
112
113 }
114 return super.onOptionsItemSelected(item);
115 }
116
117 @Override
118 public void onActivityCreated(Bundle savedInstanceState) {
119 getLoaderManager().initLoader(FORECAST_LOADER, null, this);
120 super.onActivityCreated(savedInstanceState);
121 }
122
123 private void updateWeather() {
124 String location = Utility.getPreferredLocation(getActivity());
125 new FetchWeatherTask(getActivity()).execute(location);
126 }
127
128 @Override
129 public View onCreateView(LayoutInflater inflater, ViewGroup container,
130 Bundle savedInstanceState) {
131 // The SimpleCursorAdapter will take data from the database through the
132 // Loader and use it to populate the ListView it's attached to.
133 mForecastAdapter = new SimpleCursorAdapter(
134 getActivity(),
135 R.layout.list_item_forecast,
136 null,
137 // the column names to use to fill the textviews
138 new String[]{WeatherContract.WeatherEntry.COLUMN_DATETEXT,
139 WeatherContract.WeatherEntry.COLUMN_SHORT_DESC,
140 WeatherContract.WeatherEntry.COLUMN_MAX_TEMP,
141 WeatherContract.WeatherEntry.COLUMN_MIN_TEMP
142 },
143 // the textviews to fill with the data pulled from the columns above
144 new int[]{R.id.list_item_date_textview,
145 R.id.list_item_forecast_textview,
146 R.id.list_item_high_textview,
147 R.id.list_item_low_textview
148 },
149 0
150 );
151
152 mForecastAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
153 @Override
154 public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
155 boolean isMetric = Utility.isMetric(getActivity());
156 switch (columnIndex) {
157 case COL_WEATHER_MAX_TEMP:
158 case COL_WEATHER_MIN_TEMP: {
159 // we have to do some formatting and possibly a conversion
160 ((TextView) view).setText(Utility.formatTemperature(
161 cursor.getDouble(columnIndex), isMetric));
162 return true;
163 }
164 case COL_WEATHER_DATE: {
165 String dateString = cursor.getString(columnIndex);
166 TextView dateView = (TextView) view;
167 dateView.setText(Utility.formatDate(dateString));
168 return true;
169 }
170 }
171 return false;
172 }
173 });
174
175 View rootView = inflater.inflate(R.layout.fragment_main, container, false);
176
177 ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
178 listView.setAdapter(mForecastAdapter);
179
180 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
181
182 @Override
183 public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
184 Cursor cursor = mForecastAdapter.getCursor();
185 if (cursor != null && cursor.moveToPosition(position)) {
186 Intent intent = new Intent(getActivity(), DetailActivity.class)
187 .putExtra(DetailActivity.DATE_KEY, cursor.getString(COL_WEATHER_DATE));
188 startActivity(intent);
189 }
190 }
191 });
192
193 return rootView;
194 }
195
196 @Override
197 public void onResume() {
198 super.onResume();
199 if (mLocation != null && !mLocation.equals(Utility.getPreferredLocation(getActivity()))) {
200 getLoaderManager().restartLoader(FORECAST_LOADER, null, this);
201 }
202 }
203
204 @Override
205 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
206 // This is called when a new Loader needs to be created. This
207 // fragment only uses one loader, so we don't care about checking the id.
208
209 // To only show current and future dates, get the String representation for today,
210 // and filter the query to return weather only for dates after or including today.
211 // Only return data after today.
212 String startDate = WeatherContract.getDbDateString(new Date());
213
214 // Sort order: Ascending, by date.
215 String sortOrder = WeatherEntry.COLUMN_DATETEXT + " ASC";
216
217 mLocation = Utility.getPreferredLocation(getActivity());
218 Uri weatherForLocationUri = WeatherEntry.buildWeatherLocationWithStartDate(
219 mLocation, startDate);
220
221 // Now create and return a CursorLoader that will take care of
222 // creating a Cursor for the data being displayed.
223 return new CursorLoader(
224 getActivity(),
225 weatherForLocationUri,
226 FORECAST_COLUMNS,
227 null,
228 null,
229 sortOrder
230 );
231 }
232
233 @Override
234 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
235 mForecastAdapter.swapCursor(data);
236 }
237
238 @Override
239 public void onLoaderReset(Loader<Cursor> loader) {
240 mForecastAdapter.swapCursor(null);
241 }
242 }