text 阳光网络
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了text 阳光网络相关的知识,希望对你有一定的参考价值。
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources xmlns:xliff="http://schemas.android.com/apk/res-auto">
<!-- - - - - - - - - - - - - -->
<!-- Used throughout Sunshine-->
<!-- - - - - - - - - - - - - -->
<!--Used in Action Bar, and in AndroidManifest to tell the device the name of this app.-->
<!--It's good to keep this short, so your launcher icon isn't displayed with "The greatest Wea"-->
<!--or something similar.-->
<string name="app_name">Sunshine</string>
<!--Used in overflow menu to refresh weather data-->
<string name="action_refresh">Refresh</string>
<!--COMPLETED (1) Add a string resource for your error message-->
<string name="error_message">
An error has occurred. Please try again by clicking REFRESH
</string>
<!-- - - - - - - - - - - - - -->
<!--Used by SunshineDateUtils-->
<!-- - - - - - - - - - - - - -->
<!-- For labelling today's forecast [CHAR LIMIT=15] -->
<string name="today">Today</string>
<!-- For labelling tomorrow's forecast [CHAR LIMIT=15] -->
<string name="tomorrow">Tomorrow</string>
<!-- Date format [CHAR LIMIT=NONE] -->
<string name="format_full_friendly_date">
<xliff:g id="month">%1$s</xliff:g>, <xliff:g id="day">%2$s</xliff:g>
</string>
<!-- - - - - - - - - - - - - - - -->
<!-- Used by SunshineWeatherUtils-->
<!-- - - - - - - - - - - - - - - -->
<!-- Temperature format in Celsius [CHAR LIMIT=5] -->
<string name="format_temperature_celsius">
<xliff:g id="temp">%1.0f</xliff:g>\u00B0C
</string>
<!-- Temperature format in Fahrenheit [CHAR LIMIT=5] -->
<string name="format_temperature_fahrenheit">
<xliff:g id="temp">%1.0f</xliff:g>\u00B0F
</string>
<!-- Wind in kph [CHAR LIMIT=25] -->
<string name="format_wind_kmh">
<xliff:g id="speed">%1$1.0f</xliff:g> km/h <xliff:g id="direction">%2$s</xliff:g>
</string>
<!-- Wind in mph [CHAR LIMIT=25] -->
<string name="format_wind_mph">
<xliff:g id="speed">%1$1.0f</xliff:g> mph <xliff:g id="direction">%2$s</xliff:g>
</string>
<!-- Weather Conditions (From OpenWeatherMap) -->
<string name="condition_2xx">Storm</string>
<string name="condition_3xx">Drizzle</string>
<string name="condition_500">Light Rain</string>
<string name="condition_501">Moderate Rain</string>
<string name="condition_502">Heavy Rain</string>
<string name="condition_503">Intense Rain</string>
<string name="condition_504">Extreme Rain</string>
<string name="condition_511">Freezing Rain</string>
<string name="condition_520">Light Shower</string>
<string name="condition_521">Shower</string>
<string name="condition_522">Heavy Shower</string>
<string name="condition_531">Ragged Shower</string>
<string name="condition_600">Light Snow</string>
<string name="condition_601">Snow</string>
<string name="condition_602">Heavy Snow</string>
<string name="condition_611">Sleet</string>
<string name="condition_612">Shower Sleet</string>
<string name="condition_615">Rain and Snow</string> <!-- light rain and snow -->
<string name="condition_616">Rain and Snow</string>
<string name="condition_620">Shower Snow</string> <!-- light shower snow -->
<string name="condition_621">Shower Snow</string>
<string name="condition_622">Shower Snow</string> <!-- heavy shower snow -->
<string name="condition_701">Mist</string>
<string name="condition_711">Smoke</string>
<string name="condition_721">Haze</string>
<string name="condition_731">Sand, Dust</string>
<string name="condition_741">Fog</string>
<string name="condition_751">Sand</string>
<string name="condition_761">Dust</string>
<string name="condition_762">Volcanic Ash</string>
<string name="condition_771">Squalls</string>
<string name="condition_781">Tornado</string>
<string name="condition_800">Clear</string>
<string name="condition_801">Mostly Clear</string>
<string name="condition_802">Scattered Clouds</string>
<string name="condition_803">Broken Clouds</string>
<string name="condition_804">Overcast Clouds</string>
<string name="condition_900">Tornado</string>
<string name="condition_901">Tropical Storm</string>
<string name="condition_902">Hurricane</string>
<string name="condition_903">Cold</string>
<string name="condition_904">Hot</string>
<string name="condition_905">Windy</string>
<string name="condition_906">Hail</string>
<string name="condition_951">Calm</string>
<string name="condition_952">Light Breeze</string>
<string name="condition_953">Gentle Breeze</string>
<string name="condition_954">Breeze</string> <!-- moderate breeze -->
<string name="condition_955">Fresh Breeze</string>
<string name="condition_956">Strong Breeze</string>
<string name="condition_957">High Wind</string>
<string name="condition_958">Gale</string>
<string name="condition_959">Severe Gale</string>
<string name="condition_960">Storm</string>
<string name="condition_961">Violent Storm</string>
<string name="condition_962">Hurricane</string>
<string name="condition_unknown">Unknown (<xliff:g id="conditionId">%1$d</xliff:g>)</string>
<!-- - - - - - - - - - - -->
<!-- Used by NetworkUtils-->
<!-- - - - - - - - - - - -->
<!--NONE-->
<!-- - - - - - - - - - - - - - - -->
<!-- Used by OpenWeatherJsonUtils-->
<!-- - - - - - - - - - - - - - - -->
<!--NONE-->
<!-- - - - - - - - - - - - - - -->
<!--Used by SunshinePreferences-->
<!-- - - - - - - - - - - - - - -->
<!--NONE-->
</resources>
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.sunshine.utilities;
import android.content.Context;
import android.util.Log;
import com.example.android.sunshine.R;
import com.example.android.sunshine.data.SunshinePreferences;
/**
* Contains useful utilities for a weather app, such as conversion between Celsius and Fahrenheit,
* from kph to mph, and from degrees to NSEW. It also contains the mapping of weather condition
* codes in OpenWeatherMap to strings. These strings are contained
*/
public final class SunshineWeatherUtils {
private static final String LOG_TAG = SunshineWeatherUtils.class.getSimpleName();
/**
* This method will convert a temperature from Celsius to Fahrenheit.
*
* @param temperatureInCelsius Temperature in degrees Celsius(°C)
*
* @return Temperature in degrees Fahrenheit (°F)
*/
private static double celsiusToFahrenheit(double temperatureInCelsius) {
double temperatureInFahrenheit = (temperatureInCelsius * 1.8) + 32;
return temperatureInFahrenheit;
}
/**
* Temperature data is stored in Celsius by our app. Depending on the user's preference,
* the app may need to display the temperature in Fahrenheit. This method will perform that
* temperature conversion if necessary. It will also format the temperature so that no
* decimal points show. Temperatures will be formatted to the following form: "21°C"
*
* @param context Android Context to access preferences and resources
* @param temperature Temperature in degrees Celsius (°C)
*
* @return Formatted temperature String in the following form:
* "21°C"
*/
public static String formatTemperature(Context context, double temperature) {
int temperatureFormatResourceId = R.string.format_temperature_celsius;
if (!SunshinePreferences.isMetric(context)) {
temperature = celsiusToFahrenheit(temperature);
temperatureFormatResourceId = R.string.format_temperature_fahrenheit;
}
/* For presentation, assume the user doesn't care about tenths of a degree. */
return String.format(context.getString(temperatureFormatResourceId), temperature);
}
/**
* This method will format the temperatures to be displayed in the
* following form: "HIGH°C / LOW°C"
*
* @param context Android Context to access preferences and resources
* @param high High temperature for a day in user's preferred units
* @param low Low temperature for a day in user's preferred units
*
* @return String in the form: "HIGH°C / LOW°C"
*/
public static String formatHighLows(Context context, double high, double low) {
long roundedHigh = Math.round(high);
long roundedLow = Math.round(low);
String formattedHigh = formatTemperature(context, roundedHigh);
String formattedLow = formatTemperature(context, roundedLow);
String highLowStr = formattedHigh + " / " + formattedLow;
return highLowStr;
}
/**
* This method uses the wind direction in degrees to determine compass direction as a
* String. (eg NW) The method will return the wind String in the following form: "2 km/h SW"
*
* @param context Android Context to access preferences and resources
* @param windSpeed Wind speed in kilometers / hour
* @param degrees Degrees as measured on a compass, NOT temperature degrees!
* See https://www.mathsisfun.com/geometry/degrees.html
*
* @return Wind String in the following form: "2 km/h SW"
*/
public static String getFormattedWind(Context context, float windSpeed, float degrees) {
int windFormat = R.string.format_wind_kmh;
if (!SunshinePreferences.isMetric(context)) {
windFormat = R.string.format_wind_mph;
windSpeed = .621371192237334f * windSpeed;
}
/*
* You know what's fun, writing really long if/else statements with tons of possible
* conditions. Seriously, try it!
*/
String direction = "Unknown";
if (degrees >= 337.5 || degrees < 22.5) {
direction = "N";
} else if (degrees >= 22.5 && degrees < 67.5) {
direction = "NE";
} else if (degrees >= 67.5 && degrees < 112.5) {
direction = "E";
} else if (degrees >= 112.5 && degrees < 157.5) {
direction = "SE";
} else if (degrees >= 157.5 && degrees < 202.5) {
direction = "S";
} else if (degrees >= 202.5 && degrees < 247.5) {
direction = "SW";
} else if (degrees >= 247.5 && degrees < 292.5) {
direction = "W";
} else if (degrees >= 292.5 && degrees < 337.5) {
direction = "NW";
}
return String.format(context.getString(windFormat), windSpeed, direction);
}
/**
* Helper method to provide the string according to the weather
* condition id returned by the OpenWeatherMap call.
*
* @param context Android context
* @param weatherId from OpenWeatherMap API response
* http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes
*
* @return String for the weather condition, null if no relation is found.
*/
public static String getStringForWeatherCondition(Context context, int weatherId) {
int stringId;
if (weatherId >= 200 && weatherId <= 232) {
stringId = R.string.condition_2xx;
} else if (weatherId >= 300 && weatherId <= 321) {
stringId = R.string.condition_3xx;
} else switch (weatherId) {
case 500:
stringId = R.string.condition_500;
break;
case 501:
stringId = R.string.condition_501;
break;
case 502:
stringId = R.string.condition_502;
break;
case 503:
stringId = R.string.condition_503;
break;
case 504:
stringId = R.string.condition_504;
break;
case 511:
stringId = R.string.condition_511;
break;
case 520:
stringId = R.string.condition_520;
break;
case 531:
stringId = R.string.condition_531;
break;
case 600:
stringId = R.string.condition_600;
break;
case 601:
stringId = R.string.condition_601;
break;
case 602:
stringId = R.string.condition_602;
break;
case 611:
stringId = R.string.condition_611;
break;
case 612:
stringId = R.string.condition_612;
break;
case 615:
stringId = R.string.condition_615;
break;
case 616:
stringId = R.string.condition_616;
break;
case 620:
stringId = R.string.condition_620;
break;
case 621:
stringId = R.string.condition_621;
break;
case 622:
stringId = R.string.condition_622;
break;
case 701:
stringId = R.string.condition_701;
break;
case 711:
stringId = R.string.condition_711;
break;
case 721:
stringId = R.string.condition_721;
break;
case 731:
stringId = R.string.condition_731;
break;
case 741:
stringId = R.string.condition_741;
break;
case 751:
stringId = R.string.condition_751;
break;
case 761:
stringId = R.string.condition_761;
break;
case 762:
stringId = R.string.condition_762;
break;
case 771:
stringId = R.string.condition_771;
break;
case 781:
stringId = R.string.condition_781;
break;
case 800:
stringId = R.string.condition_800;
break;
case 801:
stringId = R.string.condition_801;
break;
case 802:
stringId = R.string.condition_802;
break;
case 803:
stringId = R.string.condition_803;
break;
case 804:
stringId = R.string.condition_804;
break;
case 900:
stringId = R.string.condition_900;
break;
case 901:
stringId = R.string.condition_901;
break;
case 902:
stringId = R.string.condition_902;
break;
case 903:
stringId = R.string.condition_903;
break;
case 904:
stringId = R.string.condition_904;
break;
case 905:
stringId = R.string.condition_905;
break;
case 906:
stringId = R.string.condition_906;
break;
case 951:
stringId = R.string.condition_951;
break;
case 952:
stringId = R.string.condition_952;
break;
case 953:
stringId = R.string.condition_953;
break;
case 954:
stringId = R.string.condition_954;
break;
case 955:
stringId = R.string.condition_955;
break;
case 956:
stringId = R.string.condition_956;
break;
case 957:
stringId = R.string.condition_957;
break;
case 958:
stringId = R.string.condition_958;
break;
case 959:
stringId = R.string.condition_959;
break;
case 960:
stringId = R.string.condition_960;
break;
case 961:
stringId = R.string.condition_961;
break;
case 962:
stringId = R.string.condition_962;
break;
default:
return context.getString(R.string.condition_unknown, weatherId);
}
return context.getString(stringId);
}
/**
* Helper method to provide the icon resource id according to the weather condition id returned
* by the OpenWeatherMap call.
*
* @param weatherId from OpenWeatherMap API response
*
* @return resource id for the corresponding icon. -1 if no relation is found.
*/
public static int getIconResourceForWeatherCondition(int weatherId) {
/*
* Based on weather code data found at:
* See http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes
*/
if (weatherId >= 200 && weatherId <= 232) {
return R.drawable.ic_storm;
} else if (weatherId >= 300 && weatherId <= 321) {
return R.drawable.ic_light_rain;
} else if (weatherId >= 500 && weatherId <= 504) {
return R.drawable.ic_rain;
} else if (weatherId == 511) {
return R.drawable.ic_snow;
} else if (weatherId >= 520 && weatherId <= 531) {
return R.drawable.ic_rain;
} else if (weatherId >= 600 && weatherId <= 622) {
return R.drawable.ic_snow;
} else if (weatherId >= 701 && weatherId <= 761) {
return R.drawable.ic_fog;
} else if (weatherId == 761 || weatherId == 781) {
return R.drawable.ic_storm;
} else if (weatherId == 800) {
return R.drawable.ic_clear;
} else if (weatherId == 801) {
return R.drawable.ic_light_clouds;
} else if (weatherId >= 802 && weatherId <= 804) {
return R.drawable.ic_cloudy;
}
return -1;
}
/**
* Helper method to provide the art resource id according to the weather condition id returned
* by the OpenWeatherMap call.
*
* @param weatherId from OpenWeatherMap API response
*
* @return resource id for the corresponding icon. -1 if no relation is found.
*/
public static int getArtResourceForWeatherCondition(int weatherId) {
/*
* Based on weather code data found at:
* http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes
*/
if (weatherId >= 200 && weatherId <= 232) {
return R.drawable.art_storm;
} else if (weatherId >= 300 && weatherId <= 321) {
return R.drawable.art_light_rain;
} else if (weatherId >= 500 && weatherId <= 504) {
return R.drawable.art_rain;
} else if (weatherId == 511) {
return R.drawable.art_snow;
} else if (weatherId >= 520 && weatherId <= 531) {
return R.drawable.art_rain;
} else if (weatherId >= 600 && weatherId <= 622) {
return R.drawable.art_snow;
} else if (weatherId >= 701 && weatherId <= 761) {
return R.drawable.art_fog;
} else if (weatherId == 761 || weatherId == 771 || weatherId == 781) {
return R.drawable.art_storm;
} else if (weatherId == 800) {
return R.drawable.art_clear;
} else if (weatherId == 801) {
return R.drawable.art_light_clouds;
} else if (weatherId >= 802 && weatherId <= 804) {
return R.drawable.art_clouds;
} else if (weatherId >= 900 && weatherId <= 906) {
return R.drawable.art_storm;
} else if (weatherId >= 958 && weatherId <= 962) {
return R.drawable.art_storm;
} else if (weatherId >= 951 && weatherId <= 957) {
return R.drawable.art_clear;
}
Log.e(LOG_TAG, "Unknown Weather: " + weatherId);
return R.drawable.art_storm;
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.sunshine.utilities;
import android.content.Context;
import android.text.format.DateUtils;
import com.example.android.sunshine.R;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
/**
* Class for handling date conversions that are useful for Sunshine.
*/
public final class SunshineDateUtils {
public static final long SECOND_IN_MILLIS = 1000;
public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
/**
* This method returns the number of days since the epoch (January 01, 1970, 12:00 Midnight UTC)
* in UTC time from the current date.
*
* @param date A date in milliseconds in local time.
*
* @return The number of days in UTC time from the epoch.
*/
public static long getDayNumber(long date) {
TimeZone tz = TimeZone.getDefault();
long gmtOffset = tz.getOffset(date);
return (date + gmtOffset) / DAY_IN_MILLIS;
}
/**
* To make it easy to query for the exact date, we normalize all dates that go into
* the database to the start of the day in UTC time.
*
* @param date The UTC date to normalize
*
* @return The UTC date at 12 midnight
*/
public static long normalizeDate(long date) {
// Normalize the start date to the beginning of the (UTC) day in local time
long retValNew = date / DAY_IN_MILLIS * DAY_IN_MILLIS;
return retValNew;
}
/**
* Since all dates from the database are in UTC, we must convert the given date
* (in UTC timezone) to the date in the local timezone. Ths function performs that conversion
* using the TimeZone offset.
*
* @param utcDate The UTC datetime to convert to a local datetime, in milliseconds.
* @return The local date (the UTC datetime - the TimeZone offset) in milliseconds.
*/
public static long getLocalDateFromUTC(long utcDate) {
TimeZone tz = TimeZone.getDefault();
long gmtOffset = tz.getOffset(utcDate);
return utcDate - gmtOffset;
}
/**
* Since all dates from the database are in UTC, we must convert the local date to the date in
* UTC time. This function performs that conversion using the TimeZone offset.
*
* @param localDate The local datetime to convert to a UTC datetime, in milliseconds.
* @return The UTC date (the local datetime + the TimeZone offset) in milliseconds.
*/
public static long getUTCDateFromLocal(long localDate) {
TimeZone tz = TimeZone.getDefault();
long gmtOffset = tz.getOffset(localDate);
return localDate + gmtOffset;
}
/**
* 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.
* <p/>
* 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" (Mon, 8 Jun in UK, for example)
*
* @param context Context to use for resource localization
* @param dateInMillis The date in milliseconds (UTC)
* @param showFullDate Used to show a fuller-version of the date, which always contains either
* the day of the week, today, or tomorrow, in addition to the date.
*
* @return A user-friendly representation of the date such as "Today, June 8", "Tomorrow",
* or "Friday"
*/
public static String getFriendlyDateString(Context context, long dateInMillis, boolean showFullDate) {
long localDate = getLocalDateFromUTC(dateInMillis);
long dayNumber = getDayNumber(localDate);
long currentDayNumber = getDayNumber(System.currentTimeMillis());
if (dayNumber == currentDayNumber || showFullDate) {
/*
* If the date we're building the String for is today's date, the format
* is "Today, June 24"
*/
String dayName = getDayName(context, localDate);
String readableDate = getReadableDateString(context, localDate);
if (dayNumber - currentDayNumber < 2) {
/*
* Since there is no localized format that returns "Today" or "Tomorrow" in the API
* levels we have to support, we take the name of the day (from SimpleDateFormat)
* and use it to replace the date from DateUtils. This isn't guaranteed to work,
* but our testing so far has been conclusively positive.
*
* For information on a simpler API to use (on API > 18), please check out the
* documentation on DateFormat#getBestDateTimePattern(Locale, String)
* https://developer.android.com/reference/android/text/format/DateFormat.html#getBestDateTimePattern
*/
String localizedDayName = new SimpleDateFormat("EEEE").format(localDate);
return readableDate.replace(localizedDayName, dayName);
} else {
return readableDate;
}
} else if (dayNumber < currentDayNumber + 7) {
/* If the input date is less than a week in the future, just return the day name. */
return getDayName(context, localDate);
} else {
int flags = DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NO_YEAR
| DateUtils.FORMAT_ABBREV_ALL
| DateUtils.FORMAT_SHOW_WEEKDAY;
return DateUtils.formatDateTime(context, localDate, flags);
}
}
/**
* Returns a date string in the format specified, which shows a date, without a year,
* abbreviated, showing the full weekday.
*
* @param context Used by DateUtils to formate the date in the current locale
* @param timeInMillis Time in milliseconds since the epoch (local time)
*
* @return The formatted date string
*/
private static String getReadableDateString(Context context, long timeInMillis) {
int flags = DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NO_YEAR
| DateUtils.FORMAT_SHOW_WEEKDAY;
return DateUtils.formatDateTime(context, timeInMillis, flags);
}
/**
* 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 (local time)
*
* @return the string day of the week
*/
private static String getDayName(Context context, long dateInMillis) {
/*
* If the date is today, return the localized version of "Today" instead of the actual
* day name.
*/
long dayNumber = getDayNumber(dateInMillis);
long currentDayNumber = getDayNumber(System.currentTimeMillis());
if (dayNumber == currentDayNumber) {
return context.getString(R.string.today);
} else if (dayNumber == currentDayNumber + 1) {
return context.getString(R.string.tomorrow);
} else {
/*
* Otherwise, if the day is not today, the format is just the day of the week
* (e.g "Wednesday")
*/
SimpleDateFormat dayFormat = new SimpleDateFormat("EEEE");
return dayFormat.format(dateInMillis);
}
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.sunshine.utilities;
import android.content.ContentValues;
import android.content.Context;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.HttpURLConnection;
/**
* Utility functions to handle OpenWeatherMap JSON data.
*/
public final class OpenWeatherJsonUtils {
/**
* This method parses JSON from a web response and returns an array of Strings
* describing the weather over various days from the forecast.
* <p/>
* Later on, we'll be parsing the JSON into structured data within the
* getFullWeatherDataFromJson function, leveraging the data we have stored in the JSON. For
* now, we just convert the JSON into human-readable strings.
*
* @param forecastJsonStr JSON response from server
*
* @return Array of Strings describing weather data
*
* @throws JSONException If JSON data cannot be properly parsed
*/
public static String[] getSimpleWeatherStringsFromJson(Context context, String forecastJsonStr)
throws JSONException {
/* Weather information. Each day's forecast info is an element of the "list" array */
final String OWM_LIST = "list";
/* All temperatures are children of the "temp" object */
final String OWM_TEMPERATURE = "temp";
/* Max temperature for the day */
final String OWM_MAX = "max";
final String OWM_MIN = "min";
final String OWM_WEATHER = "weather";
final String OWM_DESCRIPTION = "main";
final String OWM_MESSAGE_CODE = "cod";
/* String array to hold each day's weather String */
String[] parsedWeatherData = null;
JSONObject forecastJson = new JSONObject(forecastJsonStr);
/* Is there an error? */
if (forecastJson.has(OWM_MESSAGE_CODE)) {
int errorCode = forecastJson.getInt(OWM_MESSAGE_CODE);
switch (errorCode) {
case HttpURLConnection.HTTP_OK:
break;
case HttpURLConnection.HTTP_NOT_FOUND:
/* Location invalid */
return null;
default:
/* Server probably down */
return null;
}
}
JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);
parsedWeatherData = new String[weatherArray.length()];
long localDate = System.currentTimeMillis();
long utcDate = SunshineDateUtils.getUTCDateFromLocal(localDate);
long startDay = SunshineDateUtils.normalizeDate(utcDate);
for (int i = 0; i < weatherArray.length(); i++) {
String date;
String highAndLow;
/* These are the values that will be collected */
long dateTimeMillis;
double high;
double low;
String description;
/* Get the JSON object representing the day */
JSONObject dayForecast = weatherArray.getJSONObject(i);
/*
* We ignore all the datetime values embedded in the JSON and assume that
* the values are returned in-order by day (which is not guaranteed to be correct).
*/
dateTimeMillis = startDay + SunshineDateUtils.DAY_IN_MILLIS * i;
date = SunshineDateUtils.getFriendlyDateString(context, dateTimeMillis, false);
/*
* Description is in a child array called "weather", which is 1 element long.
* That element also contains a weather code.
*/
JSONObject weatherObject =
dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0);
description = weatherObject.getString(OWM_DESCRIPTION);
/*
* Temperatures are sent by Open Weather Map in a child object called "temp".
*
* Editor's Note: Try not to name variables "temp" when working with temperature.
* It confuses everybody. Temp could easily mean any number of things, including
* temperature, temporary and is just a bad variable name.
*/
JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE);
high = temperatureObject.getDouble(OWM_MAX);
low = temperatureObject.getDouble(OWM_MIN);
highAndLow = SunshineWeatherUtils.formatHighLows(context, high, low);
parsedWeatherData[i] = date + " - " + description + " - " + highAndLow;
}
return parsedWeatherData;
}
/**
* Parse the JSON and convert it into ContentValues that can be inserted into our database.
*
* @param context An application context, such as a service or activity context.
* @param forecastJsonStr The JSON to parse into ContentValues.
*
* @return An array of ContentValues parsed from the JSON.
*/
public static ContentValues[] getFullWeatherDataFromJson(Context context, String forecastJsonStr) {
/** This will be implemented in a future lesson **/
return null;
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.sunshine.utilities;
import android.net.Uri;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Scanner;
/**
* These utilities will be used to communicate with the weather servers.
*/
public final class NetworkUtils {
private static final String TAG = NetworkUtils.class.getSimpleName();
private static final String DYNAMIC_WEATHER_URL =
"https://andfun-weather.udacity.com/weather";
private static final String STATIC_WEATHER_URL =
"https://andfun-weather.udacity.com/staticweather";
private static final String FORECAST_BASE_URL = STATIC_WEATHER_URL;
/*
* NOTE: These values only effect responses from OpenWeatherMap, NOT from the fake weather
* server. They are simply here to allow us to teach you how to build a URL if you were to use
* a real API.If you want to connect your app to OpenWeatherMap's API, feel free to! However,
* we are not going to show you how to do so in this course.
*/
/* The format we want our API to return */
private static final String format = "json";
/* The units we want our API to return */
private static final String units = "metric";
/* The number of days we want our API to return */
private static final int numDays = 14;
final static String QUERY_PARAM = "q";
final static String LAT_PARAM = "lat";
final static String LON_PARAM = "lon";
final static String FORMAT_PARAM = "mode";
final static String UNITS_PARAM = "units";
final static String DAYS_PARAM = "cnt";
/**
* Builds the URL used to talk to the weather server using a location. This location is based
* on the query capabilities of the weather provider that we are using.
*
* @param locationQuery The location that will be queried for.
* @return The URL to use to query the weather server.
*/
public static URL buildUrl(String locationQuery) {
Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon()
.appendQueryParameter(QUERY_PARAM, locationQuery)
.appendQueryParameter(FORMAT_PARAM, format)
.appendQueryParameter(UNITS_PARAM, units)
.appendQueryParameter(DAYS_PARAM, Integer.toString(numDays))
.build();
URL url = null;
try {
url = new URL(builtUri.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Log.v(TAG, "Built URI " + url);
return url;
}
/**
* Builds the URL used to talk to the weather server using latitude and longitude of a
* location.
*
* @param lat The latitude of the location
* @param lon The longitude of the location
* @return The Url to use to query the weather server.
*/
public static URL buildUrl(Double lat, Double lon) {
/** This will be implemented in a future lesson **/
return null;
}
/**
* This method returns the entire result from the HTTP response.
*
* @param url The URL to fetch the HTTP response from.
* @return The contents of the HTTP response.
* @throws IOException Related to network and stream reading
*/
public static String getResponseFromHttpUrl(URL url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = urlConnection.getInputStream();
Scanner scanner = new Scanner(in);
scanner.useDelimiter("\\A");
boolean hasInput = scanner.hasNext();
if (hasInput) {
return scanner.next();
} else {
return null;
}
} finally {
urlConnection.disconnect();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The menu found in the main multi-day forecast -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="@+id/action_refresh"
android:orderInCategory="1"
android:title="@string/action_refresh"
app:showAsAction="ifRoom"/>
</menu>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_weather_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textSize="20sp" />
</ScrollView>
<!--COMPLETED (2) Add a TextView that you will show the user if there is an error loading content-->
<!--COMPLETED (3) Make the width and the height wrap_content-->
<!--COMPLETED (4) Give the TextView a default error message stating that an error occurred (use strings.xml)-->
<!--COMPLETED (5) Set the default visibility of the TextView to invisible-->
<TextView
android:id="@+id/tv_error_message_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="@string/error_message"
android:textSize="20sp"
android:visibility="invisible" />
<!--COMPLETED (12) Add a ProgressBar to show the user content is loading-->
<!--COMPLETED (13) Make the width and height of the ProgressBar 42dp-->
<!--COMPLETED (14) Set the layout_gravity of the ProgressBar to center-->
<!--COMPLETED (15) Set the default visibility of the ProgressBar to invisible-->
<ProgressBar
android:id="@+id/pb_loading_indicator"
android:layout_height="42dp"
android:layout_width="42dp"
android:layout_gravity="center"
android:visibility="invisible" />
</FrameLayout>
{
"city": {
"id": 5375480,
"name": "Mountain View",
"coord": {
"lon": -122.08384,
"lat": 37.386051
},
"country": "US",
"population": 0
},
"cod": "200",
"message": 0.0158,
"cnt": 14,
"list": [
{
"dt": 1523183551.287,
"temp": {
"day": 29.49,
"min": 13.32,
"max": 18.27,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 996.68,
"humidity": 96,
"weather": [ {
"id": 500,
"main": "Light Rain",
"description": "Light Rain",
"icon": "02d"
}],
"speed": 1.2,
"deg": 0,
"clouds": 20
},
{
"dt": 1523269951.287,
"temp": {
"day": 29.49,
"min": 12.66,
"max": 17.34,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 996.12,
"humidity": 97,
"weather": [ {
"id": 501,
"main": "Moderate Rain",
"description": "Moderate Rain",
"icon": "02d"
}],
"speed": 4.8,
"deg": 45,
"clouds": 20
},
{
"dt": 1523356351.287,
"temp": {
"day": 29.49,
"min": 12.07,
"max": 16.48,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 995.7,
"humidity": 90,
"weather": [ {
"id": 800,
"main": "Clear",
"description": "Clear",
"icon": "02d"
}],
"speed": 8.2,
"deg": 90,
"clouds": 20
},
{
"dt": 1523442751.287,
"temp": {
"day": 29.49,
"min": 11.53,
"max": 12.34,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 1001.22,
"humidity": 87,
"weather": [ {
"id": 802,
"main": "Scattered Clouds",
"description": "Scattered Clouds",
"icon": "02d"
}],
"speed": 3.6,
"deg": 135,
"clouds": 20
},
{
"dt": 1523529151.287,
"temp": {
"day": 29.49,
"min": 14.62,
"max": 15.36,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 1001.51,
"humidity": 88,
"weather": [ {
"id": 803,
"main": "Broken Clouds",
"description": "Broken Clouds",
"icon": "02d"
}],
"speed": 4,
"deg": 180,
"clouds": 20
},
{
"dt": 1523615551.287,
"temp": {
"day": 29.49,
"min": -2,
"max": -1.1,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 997.54,
"humidity": 78,
"weather": [ {
"id": 600,
"main": "Light Snow",
"description": "Light Snow",
"icon": "02d"
}],
"speed": 2.6,
"deg": 225,
"clouds": 20
},
{
"dt": 1523701951.287,
"temp": {
"day": 29.49,
"min": -1.5,
"max": -1,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 996.55,
"humidity": 80,
"weather": [ {
"id": 601,
"main": "Snow",
"description": "Snow",
"icon": "02d"
}],
"speed": 3.8,
"deg": 270,
"clouds": 20
},
{
"dt": 1523788351.287,
"temp": {
"day": 29.49,
"min": -1,
"max": 0.5,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 996.76,
"humidity": 85,
"weather": [ {
"id": 602,
"main": "Heavy Snow",
"description": "Heavy Snow",
"icon": "02d"
}],
"speed": 5.2,
"deg": 315,
"clouds": 20
},
{
"dt": 1523874751.287,
"temp": {
"day": 29.49,
"min": 0.5,
"max": 2,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 998.56,
"humidity": 90,
"weather": [ {
"id": 611,
"main": "Sleet",
"description": "Sleet",
"icon": "02d"
}],
"speed": 10.3,
"deg": 0,
"clouds": 20
},
{
"dt": 1523961151.287,
"temp": {
"day": 29.49,
"min": 5,
"max": 7.5,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 1000.21,
"humidity": 80,
"weather": [ {
"id": 741,
"main": "Fog",
"description": "Fog",
"icon": "02d"
}],
"speed": 1.5,
"deg": 45,
"clouds": 20
},
{
"dt": 1524047551.287,
"temp": {
"day": 29.49,
"min": 10.21,
"max": 12.35,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 1000.11,
"humidity": 97,
"weather": [ {
"id": 960,
"main": "Storm",
"description": "Storm",
"icon": "02d"
}],
"speed": 70.2,
"deg": 90,
"clouds": 20
},
{
"dt": 1524133951.287,
"temp": {
"day": 29.49,
"min": 12.2,
"max": 19.01,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 990.01,
"humidity": 97,
"weather": [ {
"id": 960,
"main": "Storm",
"description": "Storm",
"icon": "02d"
}],
"speed": 80.1,
"deg": 135,
"clouds": 20
},
{
"dt": 1524220351.287,
"temp": {
"day": 29.49,
"min": 13,
"max": 18.01,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 989.01,
"humidity": 98,
"weather": [ {
"id": 901,
"main": "Tropical Storm",
"description": "Tropical Storm",
"icon": "02d"
}],
"speed": 110.1,
"deg": 180,
"clouds": 20
},
{
"dt": 1524306751.287,
"temp": {
"day": 29.49,
"min": 12.2,
"max": 19.01,
"night": 9.52,
"eve": 21.09,
"morn": 15.42
},
"pressure": 980.01,
"humidity": 99,
"weather": [ {
"id": 902,
"main": "Hurricane",
"description": "Hurricane",
"icon": "02d"
}],
"speed": 148.1,
"deg": 225,
"clouds": 20
}
]
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.sunshine.data;
import android.content.Context;
public class SunshinePreferences {
/*
* Human readable location string, provided by the API. Because for styling,
* "Mountain View" is more recognizable than 94043.
*/
public static final String PREF_CITY_NAME = "city_name";
/*
* In order to uniquely pinpoint the location on the map when we launch the
* map intent, we store the latitude and longitude.
*/
public static final String PREF_COORD_LAT = "coord_lat";
public static final String PREF_COORD_LONG = "coord_long";
/*
* Before you implement methods to return your REAL preference for location,
* we provide some default values to work with.
*/
private static final String DEFAULT_WEATHER_LOCATION = "94043,USA";
private static final double[] DEFAULT_WEATHER_COORDINATES = {37.4284, 122.0724};
private static final String DEFAULT_MAP_LOCATION =
"1600 Amphitheatre Parkway, Mountain View, CA 94043";
/**
* Helper method to handle setting location details in Preferences (City Name, Latitude,
* Longitude)
*
* @param c Context used to get the SharedPreferences
* @param cityName A human-readable city name, e.g "Mountain View"
* @param lat The latitude of the city
* @param lon The longitude of the city
*/
static public void setLocationDetails(Context c, String cityName, double lat, double lon) {
/** This will be implemented in a future lesson **/
}
/**
* Helper method to handle setting a new location in preferences. When this happens
* the database may need to be cleared.
*
* @param c Context used to get the SharedPreferences
* @param locationSetting The location string used to request updates from the server.
* @param lat The latitude of the city
* @param lon The longitude of the city
*/
static public void setLocation(Context c, String locationSetting, double lat, double lon) {
/** This will be implemented in a future lesson **/
}
/**
* Resets the stored location coordinates.
*
* @param c Context used to get the SharedPreferences
*/
static public void resetLocationCoordinates(Context c) {
/** This will be implemented in a future lesson **/
}
/**
* Returns the location currently set in Preferences. The default location this method
* will return is "94043,USA", which is Mountain View, California. Mountain View is the
* home of the headquarters of the Googleplex!
*
* @param context Context used to get the SharedPreferences
* @return Location The current user has set in SharedPreferences. Will default to
* "94043,USA" if SharedPreferences have not been implemented yet.
*/
public static String getPreferredWeatherLocation(Context context) {
/** This will be implemented in a future lesson **/
return getDefaultWeatherLocation();
}
/**
* Returns true if the user has selected metric temperature display.
*
* @param context Context used to get the SharedPreferences
* @return true If metric display should be used
*/
public static boolean isMetric(Context context) {
/** This will be implemented in a future lesson **/
return true;
}
/**
* Returns the location coordinates associated with the location. Note that these coordinates
* may not be set, which results in (0,0) being returned. (conveniently, 0,0 is in the middle
* of the ocean off the west coast of Africa)
*
* @param context Used to get the SharedPreferences
* @return An array containing the two coordinate values.
*/
public static double[] getLocationCoordinates(Context context) {
return getDefaultWeatherCoordinates();
}
/**
* Returns true if the latitude and longitude values are available. The latitude and
* longitude will not be available until the lesson where the PlacePicker API is taught.
*
* @param context used to get the SharedPreferences
* @return true if lat/long are set
*/
public static boolean isLocationLatLonAvailable(Context context) {
/** This will be implemented in a future lesson **/
return false;
}
private static String getDefaultWeatherLocation() {
/** This will be implemented in a future lesson **/
return DEFAULT_WEATHER_LOCATION;
}
public static double[] getDefaultWeatherCoordinates() {
/** This will be implemented in a future lesson **/
return DEFAULT_WEATHER_COORDINATES;
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.sunshine;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.android.sunshine.data.SunshinePreferences;
import com.example.android.sunshine.utilities.NetworkUtils;
import com.example.android.sunshine.utilities.OpenWeatherJsonUtils;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private TextView mWeatherTextView;
// COMPLETED (6) Add a TextView variable for the error message display
private TextView mErrorMessageDisplay;
// COMPLETED (16) Add a ProgressBar variable to show and hide the progress bar
private ProgressBar mLoadingIndicator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forecast);
/*
* Using findViewById, we get a reference to our TextView from xml. This allows us to
* do things like set the text of the TextView.
*/
mWeatherTextView = (TextView) findViewById(R.id.tv_weather_data);
// COMPLETED (7) Find the TextView for the error message using findViewById
/* This TextView is used to display errors and will be hidden if there are no errors */
mErrorMessageDisplay = (TextView) findViewById(R.id.tv_error_message_display);
// COMPLETED (17) Find the ProgressBar using findViewById
/*
* The ProgressBar that will indicate to the user that we are loading data. It will be
* hidden when no data is loading.
*
* Please note: This so called "ProgressBar" isn't a bar by default. It is more of a
* circle. We didn't make the rules (or the names of Views), we just follow them.
*/
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
/* Once all of our views are setup, we can load the weather data. */
loadWeatherData();
}
/**
* This method will get the user's preferred location for weather, and then tell some
* background method to get the weather data in the background.
*/
private void loadWeatherData() {
// COMPLETED (20) Call showWeatherDataView before executing the AsyncTask
showWeatherDataView();
String location = SunshinePreferences.getPreferredWeatherLocation(this);
new FetchWeatherTask().execute(location);
}
// COMPLETED (8) Create a method called showWeatherDataView that will hide the error message and show the weather data
/**
* This method will make the View for the weather data visible and
* hide the error message.
* <p>
* Since it is okay to redundantly set the visibility of a View, we don't
* need to check whether each view is currently visible or invisible.
*/
private void showWeatherDataView() {
/* First, make sure the error is invisible */
mErrorMessageDisplay.setVisibility(View.INVISIBLE);
/* Then, make sure the weather data is visible */
mWeatherTextView.setVisibility(View.VISIBLE);
}
// COMPLETED (9) Create a method called showErrorMessage that will hide the weather data and show the error message
/**
* This method will make the error message visible and hide the weather
* View.
* <p>
* Since it is okay to redundantly set the visibility of a View, we don't
* need to check whether each view is currently visible or invisible.
*/
private void showErrorMessage() {
/* First, hide the currently visible data */
mWeatherTextView.setVisibility(View.INVISIBLE);
/* Then, show the error */
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
// COMPLETED (18) Within your AsyncTask, override the method onPreExecute and show the loading indicator
@Override
protected void onPreExecute() {
super.onPreExecute();
mLoadingIndicator.setVisibility(View.VISIBLE);
}
@Override
protected String[] doInBackground(String... params) {
/* If there's no zip code, there's nothing to look up. */
if (params.length == 0) {
return null;
}
String location = params[0];
URL weatherRequestUrl = NetworkUtils.buildUrl(location);
try {
String jsonWeatherResponse = NetworkUtils
.getResponseFromHttpUrl(weatherRequestUrl);
String[] simpleJsonWeatherData = OpenWeatherJsonUtils
.getSimpleWeatherStringsFromJson(MainActivity.this, jsonWeatherResponse);
return simpleJsonWeatherData;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(String[] weatherData) {
// COMPLETED (19) As soon as the data is finished loading, hide the loading indicator
mLoadingIndicator.setVisibility(View.INVISIBLE);
if (weatherData != null) {
// COMPLETED (11) If the weather data was not null, make sure the data view is visible
showWeatherDataView();
/*
* Iterate through the array and append the Strings to the TextView. The reason why we add
* the "\n\n\n" after the String is to give visual separation between each String in the
* TextView. Later, we'll learn about a better way to display lists of data.
*/
for (String weatherString : weatherData) {
mWeatherTextView.append((weatherString) + "\n\n\n");
}
} else {
// COMPLETED (10) If the weather data was null, show the error message
showErrorMessage();
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
/* Use AppCompatActivity's method getMenuInflater to get a handle on the menu inflater */
MenuInflater inflater = getMenuInflater();
/* Use the inflater's inflate method to inflate our menu layout to this menu */
inflater.inflate(R.menu.forecast, menu);
/* Return true so that the menu is displayed in the Toolbar */
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_refresh) {
mWeatherTextView.setText("");
loadWeatherData();
return true;
}
return super.onOptionsItemSelected(item);
}
}
How to connect to the internet
How to show a loading indicator
How to Handle Error in loading data from internet
How to add INTERNET PERMISSIONS in Android
Parsing JSON DATA
以上是关于text 阳光网络的主要内容,如果未能解决你的问题,请参考以下文章