Android Weather Forecast - Google Weather API
Hop to the actual Tutorial-Part
The Full Source
The Full Source
:rarrow:
To download the whole project, scroll to the attachments in the end of this post.

::idea: The package-structure:


Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.anddev.android.weatherforecast">
- <application android:icon="@drawable/icon">
- <activity android:name=".WeatherForecast" android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="@drawable/weather_background"
- >
- <TableLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:stretchColumns="0"
- >
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Usage like: 'City, Country'"
- android:gravity="left"
- />
- <CheckBox android:id="@+id/chk_usecelsius"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Use °C (not °F)"
- android:gravity="right"
- />
- </TableRow>
- </TableLayout>
- <TableLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:stretchColumns="0"
- >
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <EditText android:id="@+id/edit_input"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Schriesheim, Germany"
- android:singleLine="true"
- />
- <Button android:id="@+id/cmd_submit"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="OK"
- />
- </TableRow>
- </TableLayout>
- <org.anddev.android.weatherforecast.views.SingleWeatherInfoView
- android:id="@+id/weather_today"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:paddingTop="15px"
- />
- <TableLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:stretchColumns="0,1,2,3"
- android:paddingTop="15px"
- >
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <org.anddev.android.weatherforecast.views.SingleWeatherInfoView
- android:id="@+id/weather_1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- />
- <org.anddev.android.weatherforecast.views.SingleWeatherInfoView
- android:id="@+id/weather_2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- />
- <org.anddev.android.weatherforecast.views.SingleWeatherInfoView
- android:id="@+id/weather_3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- />
- <org.anddev.android.weatherforecast.views.SingleWeatherInfoView
- android:id="@+id/weather_4"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- />
- </TableRow>
- </TableLayout>
- </LinearLayout>
Parsed in 0.012 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- package org.anddev.android.weatherforecast;
- import java.net.MalformedURLException;
- import java.net.URL;
- import javax.xml.parsers.SAXParser;
- import javax.xml.parsers.SAXParserFactory;
- import org.anddev.android.weatherforecast.views.SingleWeatherInfoView;
- import org.anddev.android.weatherforecast.weather.GoogleWeatherHandler;
- import org.anddev.android.weatherforecast.weather.WeatherCurrentCondition;
- import org.anddev.android.weatherforecast.weather.WeatherForecastCondition;
- import org.anddev.android.weatherforecast.weather.WeatherSet;
- import org.anddev.android.weatherforecast.weather.WeatherUtils;
- import org.xml.sax.InputSource;
- import org.xml.sax.XMLReader;
- import android.app.Activity;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.CheckBox;
- import android.widget.EditText;
- public class WeatherForecast extends Activity {
- private final String DEBUG_TAG = "WeatherForcaster";
- private CheckBox chk_usecelsius = null;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.main);
- this.chk_usecelsius = (CheckBox) findViewById(R.id.chk_usecelsius);
- Button cmd_submit = (Button) findViewById(R.id.cmd_submit);
- cmd_submit.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- URL url;
- try {
- /* Get what user typed to the EditText. */
- String cityParamString = ((EditText) findViewById(R.id.edit_input))
- .getText().toString();
- String queryString = "http://www.google.com/ig/api?weather="
- + cityParamString;
- /* Replace blanks with HTML-Equivalent. */
- url = new URL(queryString.replace(" ", "%20"));
- /* Get a SAXParser from the SAXPArserFactory. */
- SAXParserFactory spf = SAXParserFactory.newInstance();
- SAXParser sp = spf.newSAXParser();
- /* Get the XMLReader of the SAXParser we created. */
- XMLReader xr = sp.getXMLReader();
- /*
- * Create a new ContentHandler and apply it to the
- * XML-Reader
- */
- GoogleWeatherHandler gwh = new GoogleWeatherHandler();
- xr.setContentHandler(gwh);
- /* Parse the xml-data our URL-call returned. */
- xr.parse(new InputSource(url.openStream()));
- /* Our Handler now provides the parsed weather-data to us. */
- WeatherSet ws = gwh.getWeatherSet();
- /* Update the SingleWeatherInfoView with the parsed data. */
- updateWeatherInfoView(R.id.weather_today, ws
- .getWeatherCurrentCondition());
- updateWeatherInfoView(R.id.weather_1, ws
- .getWeatherForecastConditions().get(0));
- updateWeatherInfoView(R.id.weather_2, ws
- .getWeatherForecastConditions().get(1));
- updateWeatherInfoView(R.id.weather_3, ws
- .getWeatherForecastConditions().get(2));
- updateWeatherInfoView(R.id.weather_4, ws
- .getWeatherForecastConditions().get(3));
- } catch (Exception e) {
- resetWeatherInfoViews();
- Log.e(DEBUG_TAG, "WeatherQueryError", e);
- }
- }
- });
- }
- private void updateWeatherInfoView(int aResourceID,
- WeatherForecastCondition aWFIS) throws MalformedURLException {
- /* Construct the Image-URL. */
- URL imgURL = new URL("http://www.google.com" + aWFIS.getIconURL());
- ((SingleWeatherInfoView) findViewById(aResourceID)).setRemoteImage(imgURL);
- int tempMin = aWFIS.getTempMinCelsius();
- int tempMax = aWFIS.getTempMaxCelsius();
- /* Convert from Celsius to Fahrenheit if necessary. */
- if (this.chk_usecelsius.isChecked()) {
- ((SingleWeatherInfoView) findViewById(aResourceID))
- .setTempCelciusMinMax(tempMin, tempMax);
- } else {
- tempMin = WeatherUtils.celsiusToFahrenheit(tempMin);
- tempMax = WeatherUtils.celsiusToFahrenheit(tempMax);
- ((SingleWeatherInfoView) findViewById(aResourceID))
- .setTempFahrenheitMinMax(tempMin, tempMax);
- }
- }
- private void updateWeatherInfoView(int aResourceID,
- WeatherCurrentCondition aWCIS) throws MalformedURLException {
- /* Construct the Image-URL. */
- URL imgURL = new URL("http://www.google.com" + aWCIS.getIconURL());
- ((SingleWeatherInfoView) findViewById(aResourceID)).setRemoteImage(imgURL);
- /* Convert from Celsius to Fahrenheit if necessary. */
- if (this.chk_usecelsius.isChecked()){
- ((SingleWeatherInfoView) findViewById(aResourceID))
- .setTempCelcius(aWCIS.getTempCelcius());
- }else{
- ((SingleWeatherInfoView) findViewById(aResourceID))
- .setTempFahrenheit(aWCIS.getTempFahrenheit());
- }
- }
- private void resetWeatherInfoViews() {
- ((SingleWeatherInfoView)findViewById(R.id.weather_today)).reset();
- ((SingleWeatherInfoView)findViewById(R.id.weather_1)).reset();
- ((SingleWeatherInfoView)findViewById(R.id.weather_2)).reset();
- ((SingleWeatherInfoView)findViewById(R.id.weather_3)).reset();
- ((SingleWeatherInfoView)findViewById(R.id.weather_4)).reset();
- }
- }
Parsed in 0.050 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- package org.anddev.android.weatherforecast.views;
- import java.io.BufferedInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.URL;
- import java.net.URLConnection;
- import java.util.Map;
- import org.anddev.android.weatherforecast.R;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Typeface;
- import android.util.AttributeSet;
- import android.widget.ImageView;
- import android.widget.LinearLayout;
- import android.widget.TextView;
- /**
- * The View capable of showing a WeatehrIcon + a Temperature-TextView.
- */
- public class SingleWeatherInfoView extends LinearLayout {
- // ===========================================================
- // Fields
- // ===========================================================
- private ImageView myWeatherImageView = null;
- private TextView myTempTextView = null;
- // ===========================================================
- // Constructors
- // ===========================================================
- public SingleWeatherInfoView(Context context) {
- super(context);
- }
- public SingleWeatherInfoView(Context context, AttributeSet attrs,
- Map inflateParams) {
- super(context, attrs, inflateParams);
- /* Setup the ImageView that will show weather-icon. */
- this.myWeatherImageView = new ImageView(context);
- this.myWeatherImageView.setImageDrawable(getResources().getDrawable(
- R.drawable.dunno));
- /* Setup the textView that will show the temperature. */
- this.myTempTextView = new TextView(context);
- this.myTempTextView.setText("? °C");
- this.myTempTextView.setTextSize(16);
- this.myTempTextView.setTypeface(Typeface
- .create("Tahoma", Typeface.BOLD));
- /* Add child views to this object. */
- this.addView(this.myWeatherImageView, new LinearLayout.LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
- this.addView(this.myTempTextView, new LinearLayout.LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
- }
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- public void reset() {
- this.myWeatherImageView.setImageDrawable(getResources().getDrawable(
- R.drawable.dunno));
- this.myTempTextView.setText("? °C");
- }
- /** Sets the Child-ImageView of this to the URL passed. */
- public void setRemoteImage(URL aURL) {
- try {
- URLConnection conn = aURL.openConnection();
- conn.connect();
- InputStream is = conn.getInputStream();
- BufferedInputStream bis = new BufferedInputStream(is);
- Bitmap bm = BitmapFactory.decodeStream(bis);
- bis.close();
- is.close();
- this.myWeatherImageView.setImageBitmap(bm);
- } catch (IOException e) {
- /* Reset to 'Dunno' on any error. */
- this.myWeatherImageView.setImageDrawable(getResources()
- .getDrawable(R.drawable.dunno));
- }
- }
- public void setTempCelcius(int aTemp) {
- this.myTempTextView.setText("" + aTemp + " °C");
- }
- public void setTempFahrenheit(int aTemp) {
- this.myTempTextView.setText("" + aTemp + " °F");
- }
- public void setTempFahrenheitMinMax(int aMinTemp, int aMaxTemp) {
- this.myTempTextView.setText("" + aMinTemp + "/" + aMaxTemp + " °F");
- }
- public void setTempCelciusMinMax(int aMinTemp, int aMaxTemp) {
- this.myTempTextView.setText("" + aMinTemp + "/" + aMaxTemp + " °C");
- }
- public void setTempString(String aTempString) {
- this.myTempTextView.setText(aTempString);
- }
- }
Parsed in 0.048 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- package org.anddev.android.weatherforecast.weather;
- import org.xml.sax.Attributes;
- import org.xml.sax.SAXException;
- import org.xml.sax.helpers.DefaultHandler;
- /**
- * SAXHandler capable of extracting information out of the xml-data returned by
- * the Google Weather API.
- */
- public class GoogleWeatherHandler extends DefaultHandler {
- // ===========================================================
- // Fields
- // ===========================================================
- private WeatherSet myWeatherSet = null;
- private boolean in_forecast_information = false;
- private boolean in_current_conditions = false;
- private boolean in_forecast_conditions = false;
- private boolean usingSITemperature = false; // false means Fahrenheit
- // ===========================================================
- // Constructors
- // ===========================================================
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- public WeatherSet getWeatherSet() {
- return this.myWeatherSet;
- }
- // ===========================================================
- // Methods
- // ===========================================================
- @Override
- public void startDocument() throws SAXException {
- this.myWeatherSet = new WeatherSet();
- }
- @Override
- public void endDocument() throws SAXException {
- // Nothing
- }
- @Override
- public void startElement(String namespaceURI, String localName,
- String qName, Attributes atts) throws SAXException {
- // 'Outer' Tags
- if (localName.equals("forecast_information")) {
- this.in_forecast_information = true;
- } else if (localName.equals("current_conditions")) {
- this.myWeatherSet
- .setWeatherCurrentCondition(new WeatherCurrentCondition());
- this.in_current_conditions = true;
- } else if (localName.equals("forecast_conditions")) {
- this.myWeatherSet.getWeatherForecastConditions().add(
- new WeatherForecastCondition());
- this.in_forecast_conditions = true;
- } else {
- String dataAttribute = atts.getValue("data");
- // 'Inner' Tags of "<forecast_information>"
- if (localName.equals("city")) {
- } else if (localName.equals("postal_code")) {
- } else if (localName.equals("latitude_e6")) {
- /* One could use this to convert city-name to Lat/Long. */
- } else if (localName.equals("longitude_e6")) {
- /* One could use this to convert city-name to Lat/Long. */
- } else if (localName.equals("forecast_date")) {
- } else if (localName.equals("current_date_time")) {
- } else if (localName.equals("unit_system")) {
- if (dataAttribute.equals("SI"))
- this.usingSITemperature = true;
- }
- // SHARED(!) 'Inner' Tags within "<current_conditions>" AND
- // "<forecast_conditions>"
- else if (localName.equals("day_of_week")) {
- if (this.in_current_conditions) {
- this.myWeatherSet.getWeatherCurrentCondition()
- .setDayofWeek(dataAttribute);
- } else if (this.in_forecast_conditions) {
- this.myWeatherSet.getLastWeatherForecastCondition()
- .setDayofWeek(dataAttribute);
- }
- } else if (localName.equals("icon")) {
- if (this.in_current_conditions) {
- this.myWeatherSet.getWeatherCurrentCondition().setIconURL(
- dataAttribute);
- } else if (this.in_forecast_conditions) {
- this.myWeatherSet.getLastWeatherForecastCondition()
- .setIconURL(dataAttribute);
- }
- } else if (localName.equals("condition")) {
- if (this.in_current_conditions) {
- this.myWeatherSet.getWeatherCurrentCondition()
- .setCondition(dataAttribute);
- } else if (this.in_forecast_conditions) {
- this.myWeatherSet.getLastWeatherForecastCondition()
- .setCondition(dataAttribute);
- }
- }
- // 'Inner' Tags within "<current_conditions>"
- else if (localName.equals("temp_f")) {
- this.myWeatherSet.getWeatherCurrentCondition()
- .setTempFahrenheit(Integer.parseInt(dataAttribute));
- } else if (localName.equals("temp_c")) {
- this.myWeatherSet.getWeatherCurrentCondition().setTempCelcius(
- Integer.parseInt(dataAttribute));
- } else if (localName.equals("humidity")) {
- this.myWeatherSet.getWeatherCurrentCondition().setHumidity(
- dataAttribute);
- } else if (localName.equals("wind_condition")) {
- this.myWeatherSet.getWeatherCurrentCondition()
- .setWindCondition(dataAttribute);
- }
- // 'Inner' Tags within "<forecast_conditions>"
- else if (localName.equals("low")) {
- int temp = Integer.parseInt(dataAttribute);
- if (this.usingSITemperature) {
- this.myWeatherSet.getLastWeatherForecastCondition()
- .setTempMinCelsius(temp);
- } else {
- this.myWeatherSet.getLastWeatherForecastCondition()
- .setTempMinCelsius(
- WeatherUtils.fahrenheitToCelsius(temp));
- }
- } else if (localName.equals("high")) {
- int temp = Integer.parseInt(dataAttribute);
- if (this.usingSITemperature) {
- this.myWeatherSet.getLastWeatherForecastCondition()
- .setTempMaxCelsius(temp);
- } else {
- this.myWeatherSet.getLastWeatherForecastCondition()
- .setTempMaxCelsius(
- WeatherUtils.fahrenheitToCelsius(temp));
- }
- }
- }
- }
- @Override
- public void endElement(String namespaceURI, String localName, String qName)
- throws SAXException {
- if (localName.equals("forecast_information")) {
- this.in_forecast_information = false;
- } else if (localName.equals("current_conditions")) {
- this.in_current_conditions = false;
- } else if (localName.equals("forecast_conditions")) {
- this.in_forecast_conditions = false;
- }
- }
- @Override
- public void characters(char ch[], int start, int length) {
- /*
- * Would be called on the following structure: <element>characters</element>
- */
- }
- }
Parsed in 0.058 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- package org.anddev.android.weatherforecast.weather;
- /**
- * Holds the information between the <current_conditions>-tag of what the Google
- * Weather API returned.
- */
- public class WeatherCurrentCondition {
- // ===========================================================
- // Fields
- // ===========================================================
- private String dayofWeek = null;
- private Integer tempCelcius = null;
- private Integer tempFahrenheit = null;
- private String iconURL = null;
- private String condition = null;
- private String windCondition = null;
- private String humidity = null;
- // ===========================================================
- // Constructors
- // ===========================================================
- public WeatherCurrentCondition() {
- }
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- public String getDayofWeek() {
- return this.dayofWeek;
- }
- public void setDayofWeek(String dayofWeek) {
- this.dayofWeek = dayofWeek;
- }
- public Integer getTempCelcius() {
- return this.tempCelcius;
- }
- public void setTempCelcius(Integer temp) {
- this.tempCelcius = temp;
- }
- public Integer getTempFahrenheit() {
- return this.tempFahrenheit;
- }
- public void setTempFahrenheit(Integer temp) {
- this.tempFahrenheit = temp;
- }
- public String getIconURL() {
- return this.iconURL;
- }
- public void setIconURL(String iconURL) {
- this.iconURL = iconURL;
- }
- public String getCondition() {
- return this.condition;
- }
- public void setCondition(String condition) {
- this.condition = condition;
- }
- public String getWindCondition() {
- return this.windCondition;
- }
- public void setWindCondition(String windCondition) {
- this.windCondition = windCondition;
- }
- public String getHumidity() {
- return this.humidity;
- }
- public void setHumidity(String humidity) {
- this.humidity = humidity;
- }
- }
Parsed in 0.041 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- package org.anddev.android.weatherforecast.weather;
- /**
- * Holds the information between the <forecast_conditions>-tag of what the
- * Google Weather API returned.
- */
- public class WeatherForecastCondition {
- // ===========================================================
- // Fields
- // ===========================================================
- private String dayofWeek = null;
- private Integer tempMin = null;
- private Integer tempMax = null;
- private String iconURL = null;
- private String condition = null;
- // ===========================================================
- // Constructors
- // ===========================================================
- public WeatherForecastCondition() {
- }
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- public String getDayofWeek() {
- return dayofWeek;
- }
- public void setDayofWeek(String dayofWeek) {
- this.dayofWeek = dayofWeek;
- }
- public Integer getTempMinCelsius() {
- return tempMin;
- }
- public void setTempMinCelsius(Integer tempMin) {
- this.tempMin = tempMin;
- }
- public Integer getTempMaxCelsius() {
- return tempMax;
- }
- public void setTempMaxCelsius(Integer tempMax) {
- this.tempMax = tempMax;
- }
- public String getIconURL() {
- return iconURL;
- }
- public void setIconURL(String iconURL) {
- this.iconURL = iconURL;
- }
- public String getCondition() {
- return condition;
- }
- public void setCondition(String condition) {
- this.condition = condition;
- }
- }
Parsed in 0.041 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- package org.anddev.android.weatherforecast.weather;
- import java.util.ArrayList;
- /**
- * Combines one WeatherCurrentCondition with a List of
- * WeatherForecastConditions.
- */
- public class WeatherSet {
- // ===========================================================
- // Fields
- // ===========================================================
- private WeatherCurrentCondition myCurrentCondition = null;
- private ArrayList<WeatherForecastCondition> myForecastConditions =
- new ArrayList<WeatherForecastCondition>(4);
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- public WeatherCurrentCondition getWeatherCurrentCondition() {
- return myCurrentCondition;
- }
- public void setWeatherCurrentCondition(
- WeatherCurrentCondition myCurrentWeather) {
- this.myCurrentCondition = myCurrentWeather;
- }
- public ArrayList<WeatherForecastCondition> getWeatherForecastConditions() {
- return this.myForecastConditions;
- }
- public WeatherForecastCondition getLastWeatherForecastCondition() {
- return this.myForecastConditions
- .get(this.myForecastConditions.size() - 1);
- }
- }
Parsed in 0.039 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- package org.anddev.android.weatherforecast.weather;
- /** Useful Utility in working with temperatures. (conversions). */
- public class WeatherUtils {
- public static int fahrenheitToCelsius(int tFahrenheit) {
- return (int) ((5.0f / 9.0f) * (tFahrenheit - 32));
- }
- public static int celsiusToFahrenheit(int tCelsius) {
- return (int) ((9.0f / 5.0f) * tCelsius + 32);
- }
- }
Parsed in 0.036 seconds, using GeSHi 1.0.8.4
Regards & Merry Christmas,
plusminus








