Android WeatherInformation application.
authorgu.martinm@gmail.com <gu.martinm@gmail.com>
Sun, 30 Mar 2014 04:13:08 +0000 (06:13 +0200)
committergu.martinm@gmail.com <gu.martinm@gmail.com>
Sun, 30 Mar 2014 04:13:08 +0000 (06:13 +0200)
38 files changed:
AndroidManifest.xml [new file with mode: 0644]
ic_launcher-web.png [new file with mode: 0644]
libs/android-support-v4.jar [new file with mode: 0644]
proguard-project.txt [new file with mode: 0644]
project.properties [new file with mode: 0644]
res/drawable-hdpi/ic_launcher.png [new file with mode: 0644]
res/drawable-mdpi/ic_launcher.png [new file with mode: 0644]
res/drawable-xhdpi/ic_launcher.png [new file with mode: 0644]
res/drawable-xxhdpi/ic_launcher.png [new file with mode: 0644]
res/layout/activity_main.xml [new file with mode: 0644]
res/layout/fragment_main.xml [new file with mode: 0644]
res/menu/main.xml [new file with mode: 0644]
res/values-v11/styles.xml [new file with mode: 0644]
res/values-v14/styles.xml [new file with mode: 0644]
res/values-w820dp/dimens.xml [new file with mode: 0644]
res/values/dimens.xml [new file with mode: 0644]
res/values/strings.xml [new file with mode: 0644]
res/values/styles.xml [new file with mode: 0644]
src/de/example/exampletdd/WeatherInformationActivity.java [new file with mode: 0644]
src/de/example/exampletdd/activityinterface/ErrorMessage.java [new file with mode: 0644]
src/de/example/exampletdd/activityinterface/UpdateWeatherData.java [new file with mode: 0644]
src/de/example/exampletdd/fragment/ErrorDialogFragment.java [new file with mode: 0644]
src/de/example/exampletdd/fragment/WeatherDataFragment.java [new file with mode: 0644]
src/de/example/exampletdd/httpclient/WeatherHTTPClient.java [new file with mode: 0644]
src/de/example/exampletdd/model/WeatherData.java [new file with mode: 0644]
src/de/example/exampletdd/parser/IJPOSWeatherParser.java [new file with mode: 0644]
src/de/example/exampletdd/parser/JPOSWeatherParser.java [new file with mode: 0644]
src/de/example/exampletdd/service/WeatherService.java [new file with mode: 0644]
tests/AndroidManifest.xml [new file with mode: 0644]
tests/proguard-project.txt [new file with mode: 0644]
tests/project.properties [new file with mode: 0644]
tests/res/drawable-hdpi/ic_launcher.png [new file with mode: 0644]
tests/res/drawable-ldpi/ic_launcher.png [new file with mode: 0644]
tests/res/drawable-mdpi/ic_launcher.png [new file with mode: 0644]
tests/res/drawable-xhdpi/ic_launcher.png [new file with mode: 0644]
tests/res/values/strings.xml [new file with mode: 0644]
tests/src/de/example/exampletdd/test/JPOSWeatherParserTest.java [new file with mode: 0644]
tests/src/de/example/exampletdd/test/WeatherInformationActivityUnitTest.java [new file with mode: 0644]

diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..155fbb4
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="de.example.exampletdd"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="19"
+        android:targetSdkVersion="19" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name="de.example.exampletdd.WeatherInformationActivity"
+            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>
diff --git a/ic_launcher-web.png b/ic_launcher-web.png
new file mode 100644 (file)
index 0000000..a18cbb4
Binary files /dev/null and b/ic_launcher-web.png differ
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
new file mode 100644 (file)
index 0000000..96644ed
Binary files /dev/null and b/libs/android-support-v4.jar differ
diff --git a/proguard-project.txt b/proguard-project.txt
new file mode 100644 (file)
index 0000000..f2fe155
--- /dev/null
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/project.properties b/project.properties
new file mode 100644 (file)
index 0000000..4ab1256
--- /dev/null
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..288b665
Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ
diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..6ae570b
Binary files /dev/null and b/res/drawable-mdpi/ic_launcher.png differ
diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..d4fb7cd
Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..85a6081
Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml
new file mode 100644 (file)
index 0000000..7b7418a
--- /dev/null
@@ -0,0 +1,7 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="de.example.exampletdd.MainActivity"
+    tools:ignore="MergeRootFrame" />
diff --git a/res/layout/fragment_main.xml b/res/layout/fragment_main.xml
new file mode 100644 (file)
index 0000000..a801e94
--- /dev/null
@@ -0,0 +1,107 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="de.example.exampletdd.MainActivity$PlaceholderFragment" >
+
+    <EditText
+        android:id="@+id/editTextCity"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:ems="10"
+        android:inputType="text"
+        android:labelFor="@id/editTextCity"
+        android:text="@string/text_default_city" />
+
+    <Button
+        android:id="@+id/buttonweather"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginBottom="24dp"
+        android:onClick="onClickGetWeather"
+        android:text="@string/button_weather" />
+
+    <EditText
+        android:id="@+id/editTextWeatherDescription"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/editTextCity"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="102dp"
+        android:ems="10"
+        android:inputType="none"
+        android:labelFor="@id/editTextWeatherDescription"
+        android:text="@string/text_field_description" />
+
+    <EditText
+        android:id="@+id/editTextTemperature"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/editTextWeatherDescription"
+        android:layout_centerHorizontal="true"
+        android:ems="10"
+        android:inputType="none"
+        android:labelFor="@id/editTextTemperature"
+        android:text="@string/text_field_tem" />
+
+    <EditText
+        android:id="@+id/editTextMaxTemperature"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/editTextTemperature"
+        android:layout_centerHorizontal="true"
+        android:ems="10"
+        android:inputType="none"
+        android:labelFor="@id/editTextMaxTemperature"
+        android:text="@string/text_field_tem_max" />
+
+    <EditText
+        android:id="@+id/editTextMinTemperature"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/editTextMaxTemperature"
+        android:layout_centerHorizontal="true"
+        android:ems="10"
+        android:inputType="none"
+        android:labelFor="@id/editTextMinTemperature"
+        android:text="@string/text_field_tem_min" />
+
+    <EditText
+        android:id="@+id/editTextSunRise"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/editTextMinTemperature"
+        android:layout_centerHorizontal="true"
+        android:ems="10"
+        android:inputType="none"
+        android:labelFor="@id/editTextSunRise"
+        android:text="@string/text_field_sun_rise" />
+
+    <EditText
+        android:id="@+id/editTextSunSet"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/editTextSunRise"
+        android:layout_centerHorizontal="true"
+        android:ems="10"
+        android:inputType="none"
+        android:labelFor="@id/editTextSunSet"
+        android:text="@string/text_field_sun_set" />
+
+    <ImageView
+        android:id="@+id/imageIcon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/editTextCity"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="30dp"
+        android:contentDescription="@string/icon_weather_description"
+        android:src="@drawable/ic_launcher" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/menu/main.xml b/res/menu/main.xml
new file mode 100644 (file)
index 0000000..f7ea2b7
--- /dev/null
@@ -0,0 +1,11 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context="de.example.exampletdd.MainActivity" >
+
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:showAsAction="never"
+        android:title="@string/action_settings"/>
+
+</menu>
diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml
new file mode 100644 (file)
index 0000000..3c02242
--- /dev/null
@@ -0,0 +1,11 @@
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml
new file mode 100644 (file)
index 0000000..a91fd03
--- /dev/null
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/res/values-w820dp/dimens.xml b/res/values-w820dp/dimens.xml
new file mode 100644 (file)
index 0000000..f3e7020
--- /dev/null
@@ -0,0 +1,10 @@
+<resources>
+
+    <!--
+         Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively).
+    -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644 (file)
index 0000000..55c1e59
--- /dev/null
@@ -0,0 +1,7 @@
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644 (file)
index 0000000..1e684ca
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <string name="app_name">Weather Information</string>
+    <string name="action_settings">Settings</string>
+    <string name="button_weather">Get weather</string>
+    <string name="uri_api_coord">http://api.openweathermap.org/data/{0}/weather?lat={1}&amp;lon={2}</string>
+    <string name="uri_api_city">http://api.openweathermap.org/data/{0}/weather?q={1}</string>
+    <string name="uri_api_icon">http://openweathermap.org/img/w/{0}</string>
+    <string name="api_version">2.5</string>
+    <string name="text_default_city">City,country</string>
+    <string name="text_field_city">City name</string>
+    <string name="text_field_latitude">Latitude</string>
+    <string name="text_field_longitude">Longitude</string>
+    <string name="text_field_sun_rise">Sun rise time</string>
+    <string name="text_field_sun_set">Sun set time</string>
+    <string name="text_field_description">Weather description</string>
+    <string name="text_field_tem">Temperature</string>
+    <string name="text_field_tem_min">Min temperature</string>
+    <string name="text_field_tem_max">Max temperature</string>
+    <string name="button_ok">OK</string>
+    <string name="error_dialog_connection_tiemout">Connection error timeout</string>
+    <string name="error_dialog_generic_error">Impossible to receive weather data.</string>
+    <string name="icon_weather_description">Icon weather</string>
+
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644 (file)
index 0000000..6ce89c7
--- /dev/null
@@ -0,0 +1,20 @@
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
diff --git a/src/de/example/exampletdd/WeatherInformationActivity.java b/src/de/example/exampletdd/WeatherInformationActivity.java
new file mode 100644 (file)
index 0000000..cb4e1b3
--- /dev/null
@@ -0,0 +1,112 @@
+package de.example.exampletdd;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.EditText;
+import android.widget.ImageView;
+import de.example.exampletdd.activityinterface.ErrorMessage;
+import de.example.exampletdd.activityinterface.UpdateWeatherData;
+import de.example.exampletdd.fragment.ErrorDialogFragment;
+import de.example.exampletdd.fragment.WeatherDataFragment;
+import de.example.exampletdd.model.WeatherData;
+
+public class WeatherInformationActivity extends Activity implements ErrorMessage, UpdateWeatherData {
+    EditText weatherDescription;
+    EditText temperature;
+    EditText maxTemperature;
+    EditText minTemperature;
+    EditText sunRise;
+    EditText sunSet;
+    ImageView imageIcon;
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        this.setContentView(R.layout.activity_main);
+
+        if (savedInstanceState == null) {
+            this.getFragmentManager().beginTransaction()
+            .add(R.id.container, new WeatherDataFragment()).commit();
+        }
+
+        this.weatherDescription = (EditText) this.findViewById(R.id.editTextWeatherDescription);
+        this.temperature = (EditText) this.findViewById(R.id.editTextTemperature);
+        this.maxTemperature = (EditText) this.findViewById(R.id.editTextMaxTemperature);
+        this.minTemperature = (EditText) this.findViewById(R.id.editTextMinTemperature);
+        this.sunRise = (EditText) this.findViewById(R.id.editTextSunRise);
+        this.sunSet = (EditText) this.findViewById(R.id.editTextSunSet);
+        this.imageIcon = (ImageView) this.findViewById(R.id.imageIcon);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(final Menu menu) {
+
+        // Inflate the menu; this adds items to the action bar if it is present.
+        this.getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(final MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify a parent activity in AndroidManifest.xml.
+        final int id = item.getItemId();
+        if (id == R.id.action_settings) {
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void createErrorDialog(final int title) {
+        final DialogFragment newFragment = ErrorDialogFragment
+                .newInstance(title);
+        newFragment.show(this.getFragmentManager(), "errorDialog");
+    }
+
+
+    @Override
+    public void updateWeatherData(final WeatherData weatherData) {
+
+        if (weatherData.getWeather() != null) {
+            this.weatherDescription.setText(weatherData.getWeather().getDescription());
+            double conversion = weatherData.getMain().getTemp();
+            conversion = conversion - 273.15;
+            this.temperature.setText(String.valueOf(conversion));
+            conversion = weatherData.getMain().getMaxTemp();
+            conversion = conversion - 273.15;
+            this.maxTemperature.setText(String.valueOf(conversion));
+            conversion = weatherData.getMain().getMinTemp();
+            conversion = conversion - 273.15;
+            this.minTemperature.setText(String.valueOf(conversion));
+        }
+
+        if (weatherData.getSystem() != null) {
+            long unixTime = weatherData.getSystem().getSunRiseTime();
+            Date dateUnix = new Date(unixTime);
+            String dateFormatUnix = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z").format(dateUnix);
+            this.sunRise.setText(dateFormatUnix);
+
+            unixTime = weatherData.getSystem().getSunSetTime();
+            dateUnix = new Date(unixTime);
+            dateFormatUnix = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z").format(dateUnix);
+            this.sunSet.setText(dateFormatUnix);
+        }
+
+        if (weatherData.getIconData() != null) {
+            final Bitmap icon = BitmapFactory.decodeByteArray(
+                    weatherData.getIconData(), 0,
+                    weatherData.getIconData().length);
+            this.imageIcon.setImageBitmap(icon);
+        }
+    }
+}
diff --git a/src/de/example/exampletdd/activityinterface/ErrorMessage.java b/src/de/example/exampletdd/activityinterface/ErrorMessage.java
new file mode 100644 (file)
index 0000000..50907b9
--- /dev/null
@@ -0,0 +1,6 @@
+package de.example.exampletdd.activityinterface;
+
+public interface ErrorMessage {
+
+    public void createErrorDialog(final int title);
+}
diff --git a/src/de/example/exampletdd/activityinterface/UpdateWeatherData.java b/src/de/example/exampletdd/activityinterface/UpdateWeatherData.java
new file mode 100644 (file)
index 0000000..a2c9b23
--- /dev/null
@@ -0,0 +1,8 @@
+package de.example.exampletdd.activityinterface;
+
+import de.example.exampletdd.model.WeatherData;
+
+public interface UpdateWeatherData {
+
+    public void updateWeatherData(WeatherData weatherData);
+}
diff --git a/src/de/example/exampletdd/fragment/ErrorDialogFragment.java b/src/de/example/exampletdd/fragment/ErrorDialogFragment.java
new file mode 100644 (file)
index 0000000..76cb89a
--- /dev/null
@@ -0,0 +1,38 @@
+package de.example.exampletdd.fragment;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import de.example.exampletdd.R;
+
+public class ErrorDialogFragment extends DialogFragment {
+
+    public static ErrorDialogFragment newInstance(final int title) {
+        final ErrorDialogFragment frag = new ErrorDialogFragment();
+        final Bundle args = new Bundle();
+
+        args.putInt("title", title);
+        frag.setArguments(args);
+
+        return frag;
+    }
+
+    @Override
+    public Dialog onCreateDialog(final Bundle savedInstanceState) {
+        final int title = this.getArguments().getInt("title");
+
+        return new AlertDialog.Builder(this.getActivity())
+        .setIcon(android.R.drawable.ic_dialog_alert)
+        .setTitle(title)
+        .setPositiveButton(R.string.button_ok,
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(final DialogInterface dialog,
+                    final int whichButton) {
+
+            }
+        }).create();
+    }
+}
\ No newline at end of file
diff --git a/src/de/example/exampletdd/fragment/WeatherDataFragment.java b/src/de/example/exampletdd/fragment/WeatherDataFragment.java
new file mode 100644 (file)
index 0000000..a8fcbae
--- /dev/null
@@ -0,0 +1,178 @@
+package de.example.exampletdd.fragment;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.apache.http.client.ClientProtocolException;
+import org.json.JSONException;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.net.http.AndroidHttpClient;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import de.example.exampletdd.R;
+import de.example.exampletdd.activityinterface.ErrorMessage;
+import de.example.exampletdd.activityinterface.UpdateWeatherData;
+import de.example.exampletdd.httpclient.WeatherHTTPClient;
+import de.example.exampletdd.model.WeatherData;
+import de.example.exampletdd.parser.IJPOSWeatherParser;
+import de.example.exampletdd.parser.JPOSWeatherParser;
+import de.example.exampletdd.service.WeatherService;
+
+public class WeatherDataFragment extends Fragment {
+    private final Activity currentActivity;
+
+    public WeatherDataFragment() {
+        this.currentActivity = this.getActivity();
+    }
+
+    @Override
+    public View onCreateView(final LayoutInflater inflater,
+            final ViewGroup container, final Bundle savedInstanceState) {
+        final View rootView = inflater.inflate(R.layout.fragment_main,
+                container, false);
+        return rootView;
+    }
+
+    public void onClickGetWeather(final View v) {
+
+        final IJPOSWeatherParser JPOSWeatherParser = new JPOSWeatherParser();
+        final WeatherService weatherService = new WeatherService(
+                JPOSWeatherParser);
+        final AndroidHttpClient httpClient = AndroidHttpClient
+                .newInstance("Android Weather Information Agent");
+        final WeatherHTTPClient HTTPweatherClient = new WeatherHTTPClient(
+                httpClient);
+
+        final WeatherTask weatherTask = new WeatherTask(HTTPweatherClient, weatherService);
+
+        final EditText cityCountry = (EditText) this.getActivity()
+                .findViewById(R.id.editTextCity);
+
+        weatherTask.execute(cityCountry.getText().toString());
+    }
+
+    public class WeatherTask extends AsyncTask<Object, Void, WeatherData> {
+        private static final String TAG = "JSONWeatherTask";
+        private final WeatherHTTPClient weatherHTTPClient;
+        private final WeatherService weatherService;
+
+        public WeatherTask(final WeatherHTTPClient weatherHTTPClient,
+                final WeatherService weatherService) {
+            this.weatherHTTPClient = weatherHTTPClient;
+            this.weatherService = weatherService;
+        }
+
+        @Override
+        protected WeatherData doInBackground(final Object... params) {
+            final String cityCountry = (String) params[0];
+
+            final String urlAPICity = WeatherDataFragment.this.getResources()
+                    .getString(R.string.uri_api_city);
+            final String APIVersion = WeatherDataFragment.this.getResources()
+                    .getString(R.string.api_version);
+
+            String url = this.weatherService.createURIAPICityCountry(
+                    cityCountry, urlAPICity, APIVersion);
+
+            String jsonData = null;
+            try {
+                jsonData = this.weatherHTTPClient
+                        .retrieveJSONDataFromAPI(new URL(url));
+            } catch (final ClientProtocolException e) {
+                Log.e(TAG, "WeatherHTTPClient exception: ", e);
+                ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                .createErrorDialog(R.string.error_dialog_generic_error);
+            } catch (final MalformedURLException e) {
+                Log.e(TAG, "Syntax URL exception: ", e);
+                ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                .createErrorDialog(R.string.error_dialog_generic_error);
+            } catch (final URISyntaxException e) {
+                Log.e(TAG, "WeatherHTTPClient exception: ", e);
+                ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                .createErrorDialog(R.string.error_dialog_generic_error);
+            } catch (final IOException e) {
+                Log.e(TAG, "WeatherHTTPClient exception: ", e);
+                ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                .createErrorDialog(R.string.error_dialog_generic_error);
+            } finally {
+                this.weatherHTTPClient.close();
+            }
+
+            WeatherData weatherData = null;
+            if (jsonData != null) {
+                try {
+                    weatherData = this.weatherService.retrieveWeather(jsonData);
+                } catch (final JSONException e) {
+                    Log.e(TAG, "WeatherService exception: ", e);
+                    ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                    .createErrorDialog(R.string.error_dialog_generic_error);
+                }
+
+            }
+
+            if (weatherData != null) {
+                final String icon = weatherData.getWeather().getIcon();
+                final String urlAPIicon = WeatherDataFragment.this
+                        .getResources().getString(R.string.uri_api_icon);
+
+                url = this.weatherService.createURIAPIicon(icon, urlAPIicon);
+                try {
+                    final byte[] iconData = this.weatherHTTPClient
+                            .retrieveDataFromAPI(new URL(url)).toByteArray();
+                    weatherData.setIconData(iconData);
+                } catch (final ClientProtocolException e) {
+                    Log.e(TAG, "WeatherHTTPClient exception: ", e);
+                    ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                    .createErrorDialog(R.string.error_dialog_generic_error);
+                } catch (final MalformedURLException e) {
+                    Log.e(TAG, "Syntax URL exception: ", e);
+                    ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                    .createErrorDialog(R.string.error_dialog_generic_error);
+                } catch (final URISyntaxException e) {
+                    Log.e(TAG, "WeatherHTTPClient exception: ", e);
+                    ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                    .createErrorDialog(R.string.error_dialog_generic_error);
+                } catch (final IOException e) {
+                    Log.e(TAG, "WeatherHTTPClient exception: ", e);
+                    ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                    .createErrorDialog(R.string.error_dialog_generic_error);
+                } finally {
+                    this.weatherHTTPClient.close();
+                }
+            }
+
+            return weatherData;
+        }
+
+        @Override
+        protected void onPostExecute(final WeatherData weatherData) {
+            if (weatherData != null) {
+                ((UpdateWeatherData) WeatherDataFragment.this.currentActivity)
+                .updateWeatherData(weatherData);
+            } else {
+                ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+                .createErrorDialog(R.string.error_dialog_generic_error);
+            }
+
+            this.weatherHTTPClient.close();
+        }
+
+        @Override
+        protected void onCancelled(final WeatherData weatherData) {
+            this.onCancelled();
+            ((ErrorMessage) WeatherDataFragment.this.currentActivity)
+            .createErrorDialog(R.string.error_dialog_connection_tiemout);
+
+            this.weatherHTTPClient.close();
+        }
+    }
+}
diff --git a/src/de/example/exampletdd/httpclient/WeatherHTTPClient.java b/src/de/example/exampletdd/httpclient/WeatherHTTPClient.java
new file mode 100644 (file)
index 0000000..aab1745
--- /dev/null
@@ -0,0 +1,125 @@
+package de.example.exampletdd.httpclient;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpGet;
+
+import android.net.http.AndroidHttpClient;
+
+public class WeatherHTTPClient {
+    private final AndroidHttpClient httpClient;
+
+    public WeatherHTTPClient(final AndroidHttpClient httpClient) {
+        this.httpClient = httpClient;
+    }
+
+    public String retrieveJSONDataFromAPI(final URL url)
+            throws URISyntaxException, ClientProtocolException, IOException {
+
+        final ResponseHandler<String> handler = new ResponseHandler<String>() {
+            @Override
+            public String handleResponse(
+                    final HttpResponse response)
+                            throws UnsupportedEncodingException, IOException {
+
+                if (response != null) {
+                    final HttpEntity entity = response.getEntity();
+                    if (entity != null) {
+                        final String contentEncoding = entity
+                                .getContentEncoding()
+                                .getValue();
+                        final ByteArrayOutputStream buffer = WeatherHTTPClient.this
+                                .sortResponse(response);
+                        return new String(buffer.toByteArray(), contentEncoding);
+                    }
+
+                    throw new IOException("There is no entity");
+                }
+
+                throw new IOException("There is no response");
+            }
+        };
+
+        final HttpGet httpGet = new HttpGet();
+        httpGet.setURI(url.toURI());
+
+        return this.httpClient.execute(httpGet, handler);
+    }
+
+    public ByteArrayOutputStream retrieveDataFromAPI(final URL url)
+            throws URISyntaxException, ClientProtocolException, IOException {
+        final ResponseHandler<ByteArrayOutputStream> handler = new ResponseHandler<ByteArrayOutputStream>() {
+
+            @Override
+            public ByteArrayOutputStream handleResponse(
+                    final HttpResponse response)
+                            throws UnsupportedEncodingException, IOException {
+
+                if (response != null) {
+                    final HttpEntity entity = response.getEntity();
+                    if (entity != null) {
+                        return WeatherHTTPClient.this.sortResponse(response);
+                    }
+
+                    throw new IOException("There is no entity");
+                }
+
+                throw new IOException("There is no response");
+            }
+        };
+
+        final HttpGet httpGet = new HttpGet();
+        httpGet.setURI(url.toURI());
+
+        return this.httpClient.execute(httpGet, handler);
+    }
+
+    public ByteArrayOutputStream sortResponse(final HttpResponse httpResponse)
+            throws IOException {
+
+        if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+            final HttpEntity entity = httpResponse.getEntity();
+            if (entity != null) {
+                try {
+                    return this.readInputStream(entity.getContent());
+                } finally {
+                    entity.consumeContent();
+                }
+            }
+        }
+
+        throw new IOException("Unexpected response code: "
+                + httpResponse.getStatusLine().getStatusCode());
+    }
+
+    public void close() {
+        this.httpClient.close();
+    }
+
+    private ByteArrayOutputStream readInputStream (final InputStream inputStream) throws IOException {
+        final ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
+        final int bufferSize = 1024;
+        final byte[] buffer = new byte[bufferSize];
+
+        try {
+            int len = 0;
+            while ((len = inputStream.read(buffer)) != -1) {
+                byteBuffer.write(buffer, 0, len);
+            }
+        }finally {
+            inputStream.close();
+        }
+
+        return byteBuffer;
+    }
+}
diff --git a/src/de/example/exampletdd/model/WeatherData.java b/src/de/example/exampletdd/model/WeatherData.java
new file mode 100644 (file)
index 0000000..c3187bc
--- /dev/null
@@ -0,0 +1,456 @@
+package de.example.exampletdd.model;
+
+
+public class WeatherData {
+    private final Main main;
+    private final Wind wind;
+    private final Rain rain;
+    private final Coord coord;
+    private final DataReceivingTime dataReceivingTime;
+    private final StationName stationName;
+    private final System system;
+    private final Clouds clouds;
+    private final Weather weather;
+    private byte[] iconData;
+
+
+    public static class Builder {
+        //Optional parameters
+        private Main mMain;
+        private Wind mWind;
+        private Rain mRain;
+        private Coord mCoord;
+        private DataReceivingTime mDataReceivingTime;
+        private StationName mStationName;
+        private System mSystem;
+        private Clouds mClouds;
+        private Weather mWeather;
+
+
+        public Builder setMain(final Main main) {
+            this.mMain = main;
+            return this;
+        }
+
+        public Builder setWind(final Wind wind) {
+            this.mWind = wind;
+            return this;
+        }
+
+        public Builder setRain(final Rain rain) {
+            this.mRain = rain;
+            return this;
+        }
+
+        public Builder setCoord(final Coord coord) {
+            this.mCoord = coord;
+            return this;
+        }
+
+        public Builder setDataReceivingTime(
+                final DataReceivingTime dataReceivingTime) {
+            this.mDataReceivingTime = dataReceivingTime;
+            return this;
+        }
+
+        public Builder setStationName(final StationName stationName) {
+            this.mStationName = stationName;
+            return this;
+        }
+
+        public Builder setSystem(final System system) {
+            this.mSystem = system;
+            return this;
+        }
+
+        public Builder setClouds(final Clouds clouds) {
+            this.mClouds = clouds;
+            return this;
+        }
+
+        public Builder setWeather(final Weather weather) {
+            this.mWeather = weather;
+            return this;
+        }
+
+        public WeatherData build() {
+            return new WeatherData(this);
+        }
+    }
+
+    private WeatherData(final Builder builder) {
+        this.main = builder.mMain;
+        this.wind = builder.mWind;
+        this.rain = builder.mRain;
+        this.coord = builder.mCoord;
+        this.dataReceivingTime = builder.mDataReceivingTime;
+        this.stationName = builder.mStationName;
+        this.system = builder.mSystem;
+        this.clouds = builder.mClouds;
+        this.weather = builder.mWeather;
+    }
+
+
+    @Override
+    public String toString() {
+        final StringBuilder builder2 = new StringBuilder();
+        builder2.append("WeatherData [main=").append(this.main)
+        .append(", wind=").append(this.wind).append(", rain=")
+        .append(this.rain).append(", coord=").append(this.coord)
+        .append(", dataReceivingTime=").append(this.dataReceivingTime)
+        .append(", stationName=").append(this.stationName)
+        .append(", system=").append(this.system).append(", clouds=")
+        .append(this.clouds).append(", weather=").append(this.weather)
+        .append("]");
+        return builder2.toString();
+    }
+
+    public Main getMain() {
+        return this.main;
+    }
+
+    public Wind getWind() {
+        return this.wind;
+    }
+
+    public Rain getRain() {
+        return this.rain;
+    }
+
+    public Coord getCoord() {
+        return this.coord;
+    }
+
+    public DataReceivingTime getDataReceivingTime() {
+        return this.dataReceivingTime;
+    }
+
+    public StationName getStationName() {
+        return this.stationName;
+    }
+
+    public System getSystem() {
+        return this.system;
+    }
+
+    public Clouds getClouds() {
+        return this.clouds;
+    }
+
+    public Weather getWeather() {
+        return this.weather;
+    }
+
+    public void setIconData(final byte[] iconData) {
+        this.iconData = iconData;
+    }
+
+    public byte[] getIconData() {
+        return this.iconData;
+    }
+
+    public static class Main {
+        private final double temp;
+        // Minimum temperature
+        private final double minTemp;
+        // Maximum temperature
+        private final double maxTemp;
+        // Humidity in %
+        private final double humidity;
+        // Atmospheric pressure in kPa
+        private final double pressure;
+
+        public Main(final double temp, final double minTemp, final double maxTemp,
+                final double humidity, final double pressure) {
+            this.temp = temp;
+            this.minTemp = minTemp;
+            this.maxTemp = maxTemp;
+            this.humidity = humidity;
+            this.pressure = pressure;
+        }
+
+        public double getTemp() {
+            return this.temp;
+        }
+
+        public double getMinTemp() {
+            return this.minTemp;
+        }
+
+        public double getMaxTemp() {
+            return this.maxTemp;
+        }
+
+        public double getHumidity() {
+            return this.humidity;
+        }
+
+        public double getPressure() {
+            return this.pressure;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Main [temp=").append(this.temp)
+            .append(", minTemp=").append(this.minTemp)
+            .append(", maxTemp=").append(this.maxTemp)
+            .append(", humidity=").append(this.humidity)
+            .append(", pressure=").append(this.pressure).append("]");
+            return builder.toString();
+        }
+    }
+
+    public static class Wind {
+        // Wind speed in mps
+        private final double speed;
+        // Wind direction in degrees (meteorological)
+        private final double deg;
+        // speed of wind gust
+        private final double gust;
+        // Wind direction
+        private final double var_beg;
+        // Wind direction
+        private final double var_end;
+
+        public Wind(final double speed, final double deg, final double gust,
+                final double var_beg, final double var_end) {
+            this.speed = speed;
+            this.deg = deg;
+            this.gust = gust;
+            this.var_beg = var_beg;
+            this.var_end = var_end;
+        }
+
+        public double getSpeed() {
+            return this.speed;
+        }
+
+        public double getDeg() {
+            return this.deg;
+        }
+
+        public double getGust() {
+            return this.gust;
+        }
+
+        public double getVar_beg() {
+            return this.var_beg;
+        }
+
+        public double getVar_end() {
+            return this.var_end;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Wind [speed=").append(this.speed).append(", deg=")
+            .append(this.deg).append(", gust=").append(this.gust)
+            .append(", var_beg=").append(this.var_beg)
+            .append(", var_end=").append(this.var_end).append("]");
+            return builder.toString();
+        }
+    }
+
+    public static class Rain {
+        // Period
+        private final String time;
+        // Precipitation volume for period
+        private final double ammount;
+
+        public Rain(final String time, final double ammount) {
+            this.time = time;
+            this.ammount = ammount;
+        }
+
+        public String getTime() {
+            return this.time;
+        }
+
+        public double getAmmount() {
+            return this.ammount;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Rain [time=").append(this.time)
+            .append(", ammount=").append(this.ammount).append("]");
+            return builder.toString();
+        }
+
+    }
+
+    public static class Coord {
+        // City location
+        private final double longitude;
+        private final double latitude;
+
+        public Coord(final double longitude, final double latitude) {
+            this.longitude = longitude;
+            this.latitude = latitude;
+        }
+
+        public double getLongitude() {
+            return this.longitude;
+        }
+
+        public double getLatitude() {
+            return this.latitude;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Coord [longitude=").append(this.longitude)
+            .append(", latitude=").append(this.latitude).append("]");
+            return builder.toString();
+        }
+    }
+
+    public static class DataReceivingTime {
+        // Time of data receiving in unixtime GMT
+        private final double time;
+
+        public DataReceivingTime(final double time) {
+            this.time = time;
+        }
+
+        public double getTime() {
+            return this.time;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("DataReceivingTime [time=").append(this.time)
+            .append("]");
+            return builder.toString();
+        }
+    }
+
+    public static class StationName {
+        private final String name;
+
+        public StationName(final String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return this.name;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("StationName [name=").append(this.name).append("]");
+            return builder.toString();
+        }
+    }
+
+    public static class System {
+        private final String country;
+        // Unixtime GMT
+        private final long sunRiseTime;
+        // Unixtime GMT
+        private final long sunSetTime;
+        private final double message;
+
+        public System(final String country, final long sunRiseTime,
+                final long sunSetTime, final double message) {
+            this.country = country;
+            this.sunRiseTime = sunRiseTime;
+            this.sunSetTime = sunSetTime;
+            this.message = message;
+        }
+
+        public String getCountry() {
+            return this.country;
+        }
+
+        public long getSunRiseTime() {
+            return this.sunRiseTime;
+        }
+
+        public long getSunSetTime() {
+            return this.sunSetTime;
+        }
+
+        public double getMessage() {
+            return this.message;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("System [country=").append(this.country)
+            .append(", sunRiseTime=").append(this.sunRiseTime)
+            .append(", sunSetTime=").append(this.sunSetTime)
+            .append(", message=").append(this.message).append("]");
+            return builder.toString();
+        }
+    }
+
+    public static class Clouds {
+        // Cloudiness in %
+        private final double cloudiness;
+
+        public Clouds(final double cloudiness) {
+            this.cloudiness = cloudiness;
+        }
+
+        public double getCloudiness() {
+            return this.cloudiness;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Clouds [cloudiness=").append(this.cloudiness)
+            .append("]");
+            return builder.toString();
+        }
+    }
+
+    public static class Weather {
+        private final int weatherId;
+        private final String main;
+        private final String description;
+        private final String icon;
+
+        public Weather(final int weatherId, final String main,
+                final String description, final String icon) {
+            this.weatherId = weatherId;
+            this.main = main;
+            this.description = description;
+            this.icon = icon;
+        }
+
+        public int getWeatherId() {
+            return this.weatherId;
+        }
+
+        public String getMain() {
+            return this.main;
+        }
+
+        public String getDescription() {
+            return this.description;
+        }
+
+        public String getIcon() {
+            return this.icon;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Weather [weatherId=").append(this.weatherId)
+            .append(", main=").append(this.main)
+            .append(", description=").append(this.description)
+            .append(", icon=").append(this.icon).append("]");
+            return builder.toString();
+        }
+    }
+}
diff --git a/src/de/example/exampletdd/parser/IJPOSWeatherParser.java b/src/de/example/exampletdd/parser/IJPOSWeatherParser.java
new file mode 100644 (file)
index 0000000..c351010
--- /dev/null
@@ -0,0 +1,10 @@
+package de.example.exampletdd.parser;
+
+import org.json.JSONException;
+
+import de.example.exampletdd.model.WeatherData;
+
+public interface IJPOSWeatherParser {
+
+    WeatherData retrieveWeatherFromJPOS(final String jsonData) throws JSONException;
+}
diff --git a/src/de/example/exampletdd/parser/JPOSWeatherParser.java b/src/de/example/exampletdd/parser/JPOSWeatherParser.java
new file mode 100644 (file)
index 0000000..98de7be
--- /dev/null
@@ -0,0 +1,68 @@
+package de.example.exampletdd.parser;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import de.example.exampletdd.model.WeatherData;
+
+public class JPOSWeatherParser implements IJPOSWeatherParser {
+
+    @Override
+    public WeatherData retrieveWeatherFromJPOS(final String jsonData) throws JSONException {
+        final JSONObject jsonWeatherData = new JSONObject(jsonData);
+
+        JSONObject jsonObject = jsonWeatherData.getJSONObject("coord");
+        final double longitude = jsonObject.getDouble("lon");
+        final double latitude = jsonObject.getDouble("lat");
+        final WeatherData.Coord coord = new WeatherData.Coord(longitude,
+                latitude);
+
+        jsonObject = jsonWeatherData.getJSONObject("sys");
+        final long sunRiseTime = jsonObject.getLong("sunrise");
+        final long sunSetTime = jsonObject.getLong("sunset");
+        final double message = jsonObject.getDouble("message");
+        final String country = jsonObject.getString("country");
+        final WeatherData.System system = new WeatherData.System(country,
+                sunRiseTime, sunSetTime, message);
+
+        jsonObject = jsonWeatherData.getJSONObject("weather");
+        final int id = jsonObject.getInt("id");
+        final String mainWeather = jsonObject.getString("main");
+        final String description = jsonObject.getString("description");
+        final String icon = jsonObject.getString("icon");
+        final WeatherData.Weather weather = new WeatherData.Weather(id,
+                mainWeather, description, icon);
+
+        jsonObject = jsonWeatherData.getJSONObject("main");
+        final double temp = jsonObject.getDouble("temp");
+        final double minTemp = jsonObject.getDouble("minTemp");
+        final double maxTemp = jsonObject.getDouble("maxTemp");
+        final double humidity = jsonObject.getDouble("humidity");
+        final double pressure = jsonObject.getDouble("pressure");
+        final WeatherData.Main main = new WeatherData.Main(temp, minTemp,
+                maxTemp, humidity, pressure);
+
+        jsonObject = jsonWeatherData.getJSONObject("wind");
+        final double speed = jsonObject.getDouble("speed");
+        final double deg = jsonObject.getDouble("deg");
+        final double gust = jsonObject.getDouble("gust");
+        final double var_beg = jsonObject.getDouble("var_beg");
+        final double var_end = jsonObject.getDouble("var_end");
+        final WeatherData.Wind wind = new WeatherData.Wind(speed, deg, gust,
+                var_beg, var_end);
+
+        jsonObject = jsonWeatherData.getJSONObject("clouds");
+        final double cloudiness = jsonObject.getDouble("all");
+        final WeatherData.Clouds clouds = new WeatherData.Clouds(cloudiness);
+
+        final double time = jsonObject.getDouble("time");
+        final WeatherData.DataReceivingTime dataReceivingTime =
+                new WeatherData.DataReceivingTime(time);
+
+
+        return new WeatherData.Builder().setCoord(coord).setSystem(system)
+                .setWeather(weather).setMain(main).setWind(wind)
+                .setClouds(clouds).setDataReceivingTime(dataReceivingTime)
+                .build();
+    }
+}
diff --git a/src/de/example/exampletdd/service/WeatherService.java b/src/de/example/exampletdd/service/WeatherService.java
new file mode 100644 (file)
index 0000000..3416d6e
--- /dev/null
@@ -0,0 +1,53 @@
+package de.example.exampletdd.service;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+
+import org.json.JSONException;
+
+import de.example.exampletdd.model.WeatherData;
+import de.example.exampletdd.parser.IJPOSWeatherParser;
+
+public class WeatherService {
+    private final IJPOSWeatherParser JPOSWeatherParser;
+
+    public WeatherService(final IJPOSWeatherParser JPOSWeatherParser) {
+        this.JPOSWeatherParser = JPOSWeatherParser;
+    }
+
+    public WeatherData retrieveWeather(final String jsonData) throws JSONException {
+        return this.JPOSWeatherParser.retrieveWeatherFromJPOS(jsonData);
+    }
+
+    public String createURIAPICoord(final int latitude, final int longitude,
+            final String urlAPI, final String APIVersion) {
+
+        final MessageFormat formatURIAPI = new MessageFormat(urlAPI,
+                Locale.ENGLISH);
+        final Object[] values = new Object[3];
+        values[0] = APIVersion;
+        values[1] = latitude;
+        values[2] = longitude;
+
+        return formatURIAPI.format(values);
+    }
+
+    public String createURIAPICityCountry(final String cityCountry,
+            final String urlAPI, final String APIVersion) {
+
+        final MessageFormat formatURIAPI = new MessageFormat(urlAPI, Locale.ENGLISH);
+        final Object[] values = new Object[2];
+        values[0] = APIVersion;
+        values[1] = cityCountry;
+
+        return formatURIAPI.format(values);
+    }
+
+    public String createURIAPIicon(final String icon, final String urlAPI) {
+
+        final MessageFormat formatURIAPI = new MessageFormat(urlAPI, Locale.ENGLISH);
+
+        return formatURIAPI.format(icon);
+    }
+
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..a863aaa
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="de.example.exampletdd.test"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <instrumentation
+        android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="de.example.exampletdd" />
+
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/proguard-project.txt b/tests/proguard-project.txt
new file mode 100644 (file)
index 0000000..f2fe155
--- /dev/null
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/tests/project.properties b/tests/project.properties
new file mode 100644 (file)
index 0000000..4ab1256
--- /dev/null
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
diff --git a/tests/res/drawable-hdpi/ic_launcher.png b/tests/res/drawable-hdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..96a442e
Binary files /dev/null and b/tests/res/drawable-hdpi/ic_launcher.png differ
diff --git a/tests/res/drawable-ldpi/ic_launcher.png b/tests/res/drawable-ldpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..9923872
Binary files /dev/null and b/tests/res/drawable-ldpi/ic_launcher.png differ
diff --git a/tests/res/drawable-mdpi/ic_launcher.png b/tests/res/drawable-mdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..359047d
Binary files /dev/null and b/tests/res/drawable-mdpi/ic_launcher.png differ
diff --git a/tests/res/drawable-xhdpi/ic_launcher.png b/tests/res/drawable-xhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..71c6d76
Binary files /dev/null and b/tests/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
new file mode 100644 (file)
index 0000000..00a3f66
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">Weather Information Test</string>
+
+</resources>
diff --git a/tests/src/de/example/exampletdd/test/JPOSWeatherParserTest.java b/tests/src/de/example/exampletdd/test/JPOSWeatherParserTest.java
new file mode 100644 (file)
index 0000000..42e89eb
--- /dev/null
@@ -0,0 +1,35 @@
+package de.example.exampletdd.test;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+
+import de.example.exampletdd.model.WeatherData;
+import de.example.exampletdd.parser.JPOSWeatherParser;
+
+public class JPOSWeatherParserTest extends TestCase {
+    private JPOSWeatherParser jposWeatherParser;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        this.jposWeatherParser = new JPOSWeatherParser();
+    }
+
+    public void testRetrieveWeatherFromJPOS() throws JSONException {
+        // Arrange
+        final String jsonData = "{\"coord\":{\"lon\":139,\"lat\":35}}";
+        final double longitude = 139;
+        final double latitude = 35;
+        final WeatherData.Coord coord = new WeatherData.Coord(longitude, latitude);
+        final WeatherData expectedWeather = new WeatherData.Builder().setCoord(coord).build();
+
+        // Act
+        final WeatherData finalWeather = this.jposWeatherParser.retrieveWeatherFromJPOS(jsonData);
+
+        // Assert
+        assertEquals(expectedWeather.toString(), finalWeather.toString());
+    }
+
+}
diff --git a/tests/src/de/example/exampletdd/test/WeatherInformationActivityUnitTest.java b/tests/src/de/example/exampletdd/test/WeatherInformationActivityUnitTest.java
new file mode 100644 (file)
index 0000000..90c0d07
--- /dev/null
@@ -0,0 +1,44 @@
+package de.example.exampletdd.test;
+
+import android.content.Intent;
+import android.test.ActivityUnitTestCase;
+import android.widget.Button;
+import de.example.exampletdd.WeatherInformationActivity;
+
+public class WeatherInformationActivityUnitTest extends
+        ActivityUnitTestCase<WeatherInformationActivity> {
+
+    private WeatherInformationActivity activity;
+
+    public WeatherInformationActivityUnitTest() {
+        super(WeatherInformationActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Intent intent = new Intent(this.getInstrumentation().getTargetContext(),
+                WeatherInformationActivity.class);
+        this.startActivity(intent, null, null);
+        this.activity = this.getActivity();
+    }
+
+    public void testIntentTriggerViaOnClick() {
+        final int buttonweather = de.example.exampletdd.R.id.buttonweather;
+        final Button view = (Button) this.activity.findViewById(buttonweather);
+        assertNotNull("Button Weather not allowed to be null", view);
+
+        view.performClick();
+
+        // TouchUtils cannot be used, only allowed in
+        // InstrumentationTestCase or ActivityInstrumentationTestCase2
+
+        // Check the intent which was started
+        final Intent triggeredIntent = this.getStartedActivityIntent();
+        assertNotNull("Intent was null", triggeredIntent);
+        final String data = triggeredIntent.getDataString();
+
+        assertEquals("Incorrect data passed via the intent",
+                "http://gumartinm.name", data);
+    }
+}