andbook!.pdf - Learning Android Get an anddev.org - Android-Shirt Back to index
anddev.org Header Logo
FAQ Search Top rated articles Browse Feeds anddev.org - Authors Contact Details Register Log in

Android Weather Forecast - Google Weather API - FULL SOURCE


 
       anddev.org - Android Development Community | Android Tutorials | Index -> Advanced Tutorials
Author Message
plusminus
Site Admin


Joined: 14 Nov 2007
Posts: 2067
Location: Germany

PostPosted: Mon Dec 24, 2007 3:24 pm    Post subject: Android Weather Forecast - Google Weather API - FULL SOURCE Reply with quote

Android Weather Forecast - Google Weather API
Arrow Hop to the actual Tutorial-Part Arrow Left
The Full Source

Arrow Right Arrow Right To download the whole project, scroll to the attachments in the end of this post. Arrow Left Arrow Left


Idea Designed/Tested with sdk-version: m5-rc14

Arrow Lets get it on Very Happy :
Idea The package-structure:


Idea "/AndroidManifest.xml"
XML:
<?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>


Idea "/res/layout/main.xml"
XML:
<?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>


Idea "/src/your_package_structure/WeatherForecast.java"
Java:
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();
     }
}




Idea "/src/your_package_structure/views/SingleWeatherInfoView.java"
Java:
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);
     }
}




Idea "/src/your_package_structure/weather/GoogleWeatherHandler.java"
Java:
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>
           */

     }
}




Idea "/src/your_package_structure/weather/WeatherCurrentCondition.java"
Java:
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;
     }
}




Idea "/src/your_package_structure/weather/WeatherForecastCondition.java"
Java:
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;
&nb