diff --git a/app/build.gradle b/app/build.gradle index 19167f951..f7ffae8fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,6 +17,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + buildTypes.each { + it.buildConfigField 'String', 'OPEN_WEATHER_MAP_API_KEY', MyOpenWeatherMapApiKey + } } dependencies { diff --git a/app/src/main/java/com/example/android/sunshine/app/FetchWeatherTask.java b/app/src/main/java/com/example/android/sunshine/app/FetchWeatherTask.java index bd325b2a4..c4169ee00 100644 --- a/app/src/main/java/com/example/android/sunshine/app/FetchWeatherTask.java +++ b/app/src/main/java/com/example/android/sunshine/app/FetchWeatherTask.java @@ -278,12 +278,14 @@ protected Void doInBackground(String... params) { final String FORMAT_PARAM = "mode"; final String UNITS_PARAM = "units"; final String DAYS_PARAM = "cnt"; + final String APPID_PARAM = "APPID"; Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon() .appendQueryParameter(QUERY_PARAM, params[0]) .appendQueryParameter(FORMAT_PARAM, format) .appendQueryParameter(UNITS_PARAM, units) .appendQueryParameter(DAYS_PARAM, Integer.toString(numDays)) + .appendQueryParameter(APPID_PARAM, BuildConfig.OPEN_WEATHER_MAP_API_KEY) .build(); URL url = new URL(builtUri.toString()); diff --git a/app/src/main/java/com/example/android/sunshine/app/ForecastAdapter.java b/app/src/main/java/com/example/android/sunshine/app/ForecastAdapter.java index 8b54df847..80ddf992b 100644 --- a/app/src/main/java/com/example/android/sunshine/app/ForecastAdapter.java +++ b/app/src/main/java/com/example/android/sunshine/app/ForecastAdapter.java @@ -6,6 +6,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; /** * {@link ForecastAdapter} exposes a list of weather forecasts @@ -16,29 +18,6 @@ public ForecastAdapter(Context context, Cursor c, int flags) { super(context, c, flags); } - /** - * Prepare the weather high/lows for presentation. - */ - private String formatHighLows(double high, double low) { - boolean isMetric = Utility.isMetric(mContext); - String highLowStr = Utility.formatTemperature(high, isMetric) + "/" + Utility.formatTemperature(low, isMetric); - return highLowStr; - } - - /* - This is ported from FetchWeatherTask --- but now we go straight from the cursor to the - string. - */ - private String convertCursorRowToUXFormat(Cursor cursor) { - String highAndLow = formatHighLows( - cursor.getDouble(ForecastFragment.COL_WEATHER_MAX_TEMP), - cursor.getDouble(ForecastFragment.COL_WEATHER_MIN_TEMP)); - - return Utility.formatDate(cursor.getLong(ForecastFragment.COL_WEATHER_DATE)) + - " - " + cursor.getString(ForecastFragment.COL_WEATHER_DESC) + - " - " + highAndLow; - } - /* Remember that these views are reused as needed. */ @@ -57,7 +36,35 @@ public void bindView(View view, Context context, Cursor cursor) { // our view is pretty simple here --- just a text view // we'll keep the UI functional with a simple (and slow!) binding. - //TextView tv = (TextView)view; - //tv.setText(convertCursorRowToUXFormat(cursor)); + // Read weather icon ID from cursor + int weatherId = cursor.getInt(ForecastFragment.COL_WEATHER_ID); + // Use placeholder image for now + ImageView iconView = (ImageView) view.findViewById(R.id.list_item_icon); + iconView.setImageResource(R.drawable.ic_launcher); + + // Read date from cursor + long dateInMillis = cursor.getLong(ForecastFragment.COL_WEATHER_DATE); + // Find TextView and set formatted date on it + TextView dateView = (TextView) view.findViewById(R.id.list_item_date_textview); + dateView.setText(Utility.getFriendlyDayString(context, dateInMillis)); + + // Read weather forecast from cursor + String description = cursor.getString(ForecastFragment.COL_WEATHER_DESC); + // Find TextView and set weather forecast on it + TextView descriptionView = (TextView) view.findViewById(R.id.list_item_forecast_textview); + descriptionView.setText(description); + + // Read user preference for metric or imperial temperature units + boolean isMetric = Utility.isMetric(context); + + // Read high temperature from cursor + double high = cursor.getDouble(ForecastFragment.COL_WEATHER_MAX_TEMP); + TextView highView = (TextView) view.findViewById(R.id.list_item_high_textview); + highView.setText(Utility.formatTemperature(high, isMetric)); + + // Read low temperature from cursor + double low = cursor.getDouble(ForecastFragment.COL_WEATHER_MIN_TEMP); + TextView lowView = (TextView) view.findViewById(R.id.list_item_low_textview); + lowView.setText(Utility.formatTemperature(low, isMetric)); } } \ No newline at end of file diff --git a/app/src/main/java/com/example/android/sunshine/app/Utility.java b/app/src/main/java/com/example/android/sunshine/app/Utility.java index 317d9ee18..30b9a7f21 100644 --- a/app/src/main/java/com/example/android/sunshine/app/Utility.java +++ b/app/src/main/java/com/example/android/sunshine/app/Utility.java @@ -18,8 +18,10 @@ import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.text.format.Time; import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Date; public class Utility { @@ -46,8 +48,98 @@ static String formatTemperature(double temperature, boolean isMetric) { return String.format("%.0f", temp); } - static String formatDate(long dateInMillis) { - Date date = new Date(dateInMillis); + static String formatDate(long dateInMilliseconds) { + Date date = new Date(dateInMilliseconds); return DateFormat.getDateInstance().format(date); } + + // Format used for storing dates in the database. ALso used for converting those strings + // back into date objects for comparison/processing. + public static final String DATE_FORMAT = "yyyyMMdd"; + + /** + * Helper method to convert the database representation of the date into something to display + * to users. As classy and polished a user experience as "20140102" is, we can do better. + * + * @param context Context to use for resource localization + * @param dateInMillis The date in milliseconds + * @return a user-friendly representation of the date. + */ + public static String getFriendlyDayString(Context context, long dateInMillis) { + // The day string for forecast uses the following logic: + // For today: "Today, June 8" + // For tomorrow: "Tomorrow" + // For the next 5 days: "Wednesday" (just the day name) + // For all days after that: "Mon Jun 8" + + Time time = new Time(); + time.setToNow(); + long currentTime = System.currentTimeMillis(); + int julianDay = Time.getJulianDay(dateInMillis, time.gmtoff); + int currentJulianDay = Time.getJulianDay(currentTime, time.gmtoff); + + // If the date we're building the String for is today's date, the format + // is "Today, June 24" + if (julianDay == currentJulianDay) { + String today = context.getString(R.string.today); + int formatId = R.string.format_full_friendly_date; + return String.format(context.getString( + formatId, + today, + getFormattedMonthDay(context, dateInMillis))); + } else if ( julianDay < currentJulianDay + 7 ) { + // If the input date is less than a week in the future, just return the day name. + return getDayName(context, dateInMillis); + } else { + // Otherwise, use the form "Mon Jun 3" + SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd"); + return shortenedDateFormat.format(dateInMillis); + } + } + + /** + * Given a day, returns just the name to use for that day. + * E.g "today", "tomorrow", "wednesday". + * + * @param context Context to use for resource localization + * @param dateInMillis The date in milliseconds + * @return + */ + public static String getDayName(Context context, long dateInMillis) { + // If the date is today, return the localized version of "Today" instead of the actual + // day name. + + Time t = new Time(); + t.setToNow(); + int julianDay = Time.getJulianDay(dateInMillis, t.gmtoff); + int currentJulianDay = Time.getJulianDay(System.currentTimeMillis(), t.gmtoff); + if (julianDay == currentJulianDay) { + return context.getString(R.string.today); + } else if ( julianDay == currentJulianDay +1 ) { + return context.getString(R.string.tomorrow); + } else { + Time time = new Time(); + time.setToNow(); + // Otherwise, the format is just the day of the week (e.g "Wednesday". + SimpleDateFormat dayFormat = new SimpleDateFormat("EEEE"); + return dayFormat.format(dateInMillis); + } + } + + /** + * Converts db date format to the format "Month day", e.g "June 24". + * @param context Context to use for resource localization + * @param dateInMillis The db formatted date string, expected to be of the form specified + * in Utility.DATE_FORMAT + * @return The day in the form of a string formatted "December 6" + */ + public static String getFormattedMonthDay(Context context, long dateInMillis ) { + Time time = new Time(); + time.setToNow(); + SimpleDateFormat dbDateFormat = new SimpleDateFormat(Utility.DATE_FORMAT); + SimpleDateFormat monthDayFormat = new SimpleDateFormat("MMMM dd"); + String monthDayString = monthDayFormat.format(dateInMillis); + return monthDayString; + } + } \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_forecast.xml b/app/src/main/res/layout/list_item_forecast.xml index d7bb57759..9e7515765 100644 --- a/app/src/main/res/layout/list_item_forecast.xml +++ b/app/src/main/res/layout/list_item_forecast.xml @@ -12,8 +12,7 @@ + android:layout_height="wrap_content"/> + android:layout_height="wrap_content"/> + android:layout_height="wrap_content"/> @@ -44,14 +41,12 @@ + android:layout_height="wrap_content"/> + android:layout_height="wrap_content"/> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14ae616c2..12a2c8bf6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - + imperial - + + + Today + + + Tomorrow + + + %1$s, %2$s + + \ No newline at end of file