1 package uk
.me
.njae
.sunshine
;
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
;
24 import java
.io
.UnsupportedEncodingException
;
25 import java
.net
.URLEncoder
;
26 import java
.util
.Date
;
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
;
32 import static android
.util
.Log
.e
;
35 * A placeholder fragment containing a simple view.
37 public class ForecastFragment
extends Fragment
implements LoaderManager
.LoaderCallbacks
<Cursor
> {
39 private final String LOG_TAG
= ForecastFragment
.class.getSimpleName();
41 private SimpleCursorAdapter mForecastAdapter
;
43 private static final int FORECAST_LOADER
= 0;
44 private String mLocation
;
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
63 // These indices are tied to FORECAST_COLUMNS. If FORECAST_COLUMNS changes, these
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;
72 public ForecastFragment() {
76 public void onCreate(Bundle savedInstanceState
) {
77 super.onCreate(savedInstanceState
);
78 setHasOptionsMenu(true);
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
);
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
) {
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
);
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
);
109 if (intent
.resolveActivity(getActivity().getPackageManager()) != null) {
110 startActivity(intent
);
114 return super.onOptionsItemSelected(item
);
118 public void onActivityCreated(Bundle savedInstanceState
) {
119 getLoaderManager().initLoader(FORECAST_LOADER
, null, this);
120 super.onActivityCreated(savedInstanceState
);
123 private void updateWeather() {
124 String location
= Utility
.getPreferredLocation(getActivity());
125 new FetchWeatherTask(getActivity()).execute(location
);
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(
135 R
.layout
.list_item_forecast
,
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
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
152 mForecastAdapter
.setViewBinder(new SimpleCursorAdapter
.ViewBinder() {
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
));
164 case COL_WEATHER_DATE
: {
165 String dateString
= cursor
.getString(columnIndex
);
166 TextView dateView
= (TextView
) view
;
167 dateView
.setText(Utility
.formatDate(dateString
));
175 View rootView
= inflater
.inflate(R
.layout
.fragment_main
, container
, false);
177 ListView listView
= (ListView
) rootView
.findViewById(R
.id
.listview_forecast
);
178 listView
.setAdapter(mForecastAdapter
);
180 listView
.setOnItemClickListener(new AdapterView
.OnItemClickListener() {
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
);
197 public void onResume() {
199 if (mLocation
!= null && !mLocation
.equals(Utility
.getPreferredLocation(getActivity()))) {
200 getLoaderManager().restartLoader(FORECAST_LOADER
, null, this);
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.
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());
214 // Sort order: Ascending, by date.
215 String sortOrder
= WeatherEntry
.COLUMN_DATETEXT
+ " ASC";
217 mLocation
= Utility
.getPreferredLocation(getActivity());
218 Uri weatherForLocationUri
= WeatherEntry
.buildWeatherLocationWithStartDate(
219 mLocation
, startDate
);
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(
225 weatherForLocationUri
,
234 public void onLoadFinished(Loader
<Cursor
> loader
, Cursor data
) {
235 mForecastAdapter
.swapCursor(data
);
239 public void onLoaderReset(Loader
<Cursor
> loader
) {
240 mForecastAdapter
.swapCursor(null);