Weather Information with Google Maps
authorgu.martinm@gmail.com <gu.martinm@gmail.com>
Mon, 7 Apr 2014 04:21:31 +0000 (06:21 +0200)
committergu.martinm@gmail.com <gu.martinm@gmail.com>
Mon, 7 Apr 2014 04:21:31 +0000 (06:21 +0200)
20 files changed:
Android/WeatherInformation/AndroidManifest.xml
Android/WeatherInformation/proguard-project.txt
Android/WeatherInformation/project.properties
Android/WeatherInformation/res/drawable-hdpi/ic_action_map.png [new file with mode: 0644]
Android/WeatherInformation/res/drawable-mdpi/ic_action_map.png [new file with mode: 0644]
Android/WeatherInformation/res/drawable-xhdpi/ic_action_map.png [new file with mode: 0644]
Android/WeatherInformation/res/drawable-xxhdpi/ic_action_map.png [new file with mode: 0644]
Android/WeatherInformation/res/layout/fragment_main.xml [deleted file]
Android/WeatherInformation/res/layout/weather_map.xml [new file with mode: 0644]
Android/WeatherInformation/res/menu/weather_main_menu.xml
Android/WeatherInformation/res/values/strings.xml
Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationActivity.java
Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationMapActivity.java [new file with mode: 0644]
Android/WeatherInformation/src/de/example/exampletdd/activityinterface/GetWeather.java [new file with mode: 0644]
Android/WeatherInformation/src/de/example/exampletdd/activityinterface/OnClickButtons.java [deleted file]
Android/WeatherInformation/src/de/example/exampletdd/fragment/WeatherInformationDataFragment.java
Android/WeatherInformation/src/de/example/exampletdd/model/GeocodingData.java [new file with mode: 0644]
Android/WeatherInformation/src/de/example/exampletdd/parser/IJPOSWeatherParser.java
Android/WeatherInformation/src/de/example/exampletdd/parser/JPOSWeatherParser.java
Android/WeatherInformation/src/de/example/exampletdd/service/WeatherService.java

index 8ae62c1..811923f 100644 (file)
@@ -9,8 +9,18 @@
         android:maxSdkVersion="18"
         android:minSdkVersion="18"
         android:targetSdkVersion="18" />
+    <uses-feature
+        android:glEsVersion="0x00020000"
+        android:required="true"/>
 
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <!-- The following two permissions are not required to use
+     Google Maps Android API v2, but are recommended. -->
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
 
     <application
         android:allowBackup="true"
             </intent-filter>
         </activity>
         
+        <activity
+            android:name=".WeatherInformationMapActivity" 
+            android:parentActivityName="de.example.exampletdd.WeatherInformationActivity">
+            <intent-filter >
+                <action android:name="android.intent.action.WEATHERINFORMATIONMAP" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <meta-data
+            android:name="com.google.android.maps.v2.API_KEY"
+            android:value="AIzaSyBNYGgEZgS3cqEaElaegEtOlY57jhLeEqU"/>
+        <meta-data
+            android:name="com.google.android.gms.version"
+            android:value="@integer/google_play_services_version" />
     </application>
 
 </manifest>
index f2fe155..dd79e3a 100644 (file)
 #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 #   public *;
 #}
+
+-keep class * extends java.util.ListResourceBundle {
+    protected Object[][] getContents();
+}
+
+-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
+    public static final *** NULL;
+}
+
+-keepnames @com.google.android.gms.common.annotation.KeepName class *
+-keepclassmembernames class * {
+    @com.google.android.gms.common.annotation.KeepName *;
+}
+
+-keepnames class * implements android.os.Parcelable {
+    public static final ** CREATOR;
+}
index ce39f2d..2110cdf 100644 (file)
@@ -12,3 +12,4 @@
 
 # Project target.
 target=android-18
+android.library.reference.1=../../../../android/android-sdk-linux/extras/google/google_play_services/libproject/google-play-services_lib
diff --git a/Android/WeatherInformation/res/drawable-hdpi/ic_action_map.png b/Android/WeatherInformation/res/drawable-hdpi/ic_action_map.png
new file mode 100644 (file)
index 0000000..370cf5c
Binary files /dev/null and b/Android/WeatherInformation/res/drawable-hdpi/ic_action_map.png differ
diff --git a/Android/WeatherInformation/res/drawable-mdpi/ic_action_map.png b/Android/WeatherInformation/res/drawable-mdpi/ic_action_map.png
new file mode 100644 (file)
index 0000000..50a9100
Binary files /dev/null and b/Android/WeatherInformation/res/drawable-mdpi/ic_action_map.png differ
diff --git a/Android/WeatherInformation/res/drawable-xhdpi/ic_action_map.png b/Android/WeatherInformation/res/drawable-xhdpi/ic_action_map.png
new file mode 100644 (file)
index 0000000..537c5a4
Binary files /dev/null and b/Android/WeatherInformation/res/drawable-xhdpi/ic_action_map.png differ
diff --git a/Android/WeatherInformation/res/drawable-xxhdpi/ic_action_map.png b/Android/WeatherInformation/res/drawable-xxhdpi/ic_action_map.png
new file mode 100644 (file)
index 0000000..ed72ce9
Binary files /dev/null and b/Android/WeatherInformation/res/drawable-xxhdpi/ic_action_map.png differ
diff --git a/Android/WeatherInformation/res/layout/fragment_main.xml b/Android/WeatherInformation/res/layout/fragment_main.xml
deleted file mode 100644 (file)
index b110cec..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-<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.fragment.WeatherInformationDataFragment" >
-
-    <EditText
-        android:id="@+id/editTextCity"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:background="@android:color/transparent"
-        android:ems="10"
-        android:inputType="text"
-        android:labelFor="@id/editTextCity"
-        android:text="@string/text_default_city"
-        android:textStyle="bold" />
-
-    <EditText
-        android:id="@+id/editTextWeatherDescription"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@+id/editTextCity"
-        android:layout_centerHorizontal="true"
-        android:layout_marginTop="102dp"
-        android:background="@android:color/transparent"
-        android:ems="10"
-        android:enabled="false"
-        android:inputType="none"
-        android:labelFor="@id/editTextWeatherDescription"
-        android:text="@string/text_field_description"
-        android:textStyle="bold" />
-
-    <ImageView
-        android:id="@+id/imageIcon"
-        android:layout_width="100dp"
-        android:layout_height="100dp"
-        android:layout_below="@+id/editTextCity"
-        android:layout_centerHorizontal="true"
-        android:contentDescription="@string/icon_weather_description"
-        android:scaleType="fitCenter"
-        android:src="@drawable/ic_launcher" />
-
-    <EditText
-        android:id="@+id/editTextSunSet"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_above="@+id/buttonweather"
-        android:layout_alignLeft="@+id/editTextSunRise"
-        android:layout_marginBottom="18dp"
-        android:background="@android:color/transparent"
-        android:ems="10"
-        android:enabled="false"
-        android:inputType="none"
-        android:labelFor="@id/editTextSunSet"
-        android:text="@string/text_field_sun_set"
-        android:textStyle="bold" />
-
-    <EditText
-        android:id="@+id/editTextSunRise"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_above="@+id/editTextSunSet"
-        android:layout_alignLeft="@+id/editTextMinTemperature"
-        android:layout_marginBottom="18dp"
-        android:background="@android:color/transparent"
-        android:ems="10"
-        android:enabled="false"
-        android:inputType="none"
-        android:labelFor="@id/editTextSunRise"
-        android:text="@string/text_field_sun_rise"
-        android:textStyle="bold" />
-
-    <EditText
-        android:id="@+id/editTextMinTemperature"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_above="@+id/editTextSunRise"
-        android:layout_alignLeft="@+id/editTextMaxTemperature"
-        android:layout_marginBottom="18dp"
-        android:background="@android:color/transparent"
-        android:ems="10"
-        android:enabled="false"
-        android:inputType="none"
-        android:labelFor="@id/editTextMinTemperature"
-        android:text="@string/text_field_tem_min"
-        android:textStyle="bold" />
-
-    <EditText
-        android:id="@+id/editTextMaxTemperature"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignLeft="@+id/editTextTemperature"
-        android:layout_centerVertical="true"
-        android:background="@android:color/transparent"
-        android:ems="10"
-        android:enabled="false"
-        android:inputType="none"
-        android:labelFor="@id/editTextMaxTemperature"
-        android:text="@string/text_field_tem_max"
-        android:textStyle="bold" />
-
-    <EditText
-        android:id="@+id/editTextTemperature"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignLeft="@+id/editTextWeatherDescription"
-        android:layout_below="@+id/editTextWeatherDescription"
-        android:layout_marginTop="19dp"
-        android:background="@android:color/transparent"
-        android:ems="10"
-        android:enabled="false"
-        android:inputType="none"
-        android:labelFor="@id/editTextTemperature"
-        android:text="@string/text_field_tem"
-        android:textStyle="bold" />
-
-    <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="19dp"
-        android:onClick="onClickGetWeather"
-        android:text="@string/button_weather" />
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/Android/WeatherInformation/res/layout/weather_map.xml b/Android/WeatherInformation/res/layout/weather_map.xml
new file mode 100644 (file)
index 0000000..62c94d0
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>    
+<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:layout_gravity="bottom"
+    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.WeatherInformationActivity" >
+
+    <TextView
+        android:id="@+id/weather_map_citycountry_header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="top"
+        android:text="City,country:"
+        android:textAlignment="textStart"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textIsSelectable="false"
+        android:textStyle="normal" />
+    
+    <TextView
+        android:id="@+id/weather_map_citycountry_data"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/weather_map_citycountry_header"
+        android:gravity="top"
+        android:text="City,country"
+        android:textAlignment="textStart"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textStyle="bold|normal" />
+
+    <fragment
+        android:id="@+id/map"
+        android:name="com.google.android.gms.maps.MapFragment"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_above="@+id/buttonweather"
+        android:layout_below="@+id/weather_map_citycountry_data" />
+       
+    <Button
+        android:id="@+id/buttonweather"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:onClick="setChosenLocation"
+        android:text="@string/button_set_chosen_location" />
+
+
+</RelativeLayout>
index 6e3221f..3b6359d 100644 (file)
         android:icon="@drawable/ic_action_settings"
         android:showAsAction="ifRoom|withText">
     </item>
+    <item
+        android:id="@+id/weather_menu_map"
+        android:icon="@drawable/ic_action_map"
+        android:showAsAction="ifRoom|withText"
+        android:visible="true"
+        android:checkable="false"
+        android:enabled="true"
+        android:checked="false">
+    </item>
     
     
 
index 8a39ec6..f0475fe 100644 (file)
@@ -5,10 +5,12 @@
     <string name="action_settings">Settings</string>
     <string name="action_search">City,country</string>
     <string name="action_get_weather">Get weather</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="action_map">Select Location</string>
+    <string name="button_set_chosen_location">Set Location</string>
+    <string name="uri_api_coord">http://api.openweathermap.org/data/{0}/weather?lat={1}&amp;lon={2}&amp;lang={3}&amp;cnt=1</string>
     <string name="uri_api_city">http://api.openweathermap.org/data/{0}/weather?q={1}&amp;lang={2}</string>
     <string name="uri_api_icon">http://openweathermap.org/img/w/{0}.png</string>
+    <string name="uri_api_geocoding">https://maps.googleapis.com/maps/api/geocode/json?latlng={0},{1}&amp;sensor=true</string>
     <string name="api_version">2.5</string>
     <string name="text_default_city">London,uk</string>
     <string name="text_field_city">City name</string>
@@ -28,6 +30,7 @@
     <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="error_dialog_store_geocoding_data">Impossible to save current location.</string>
     <string name="icon_weather_description">Icon weather</string>
     <string name="header_action_bar">City,country</string>
     <string name="weather_preferences_units_key">weather_preferences_units</string>
index e693053..876de1c 100644 (file)
@@ -10,12 +10,12 @@ import android.preference.PreferenceManager;
 import android.view.Menu;
 import android.view.MenuItem;
 import de.example.exampletdd.activityinterface.ErrorMessage;
-import de.example.exampletdd.activityinterface.OnClickButtons;
+import de.example.exampletdd.activityinterface.GetWeather;
 import de.example.exampletdd.fragment.ErrorDialogFragment;
 import de.example.exampletdd.fragment.WeatherInformationDataFragment;
 
 public class WeatherInformationActivity extends Activity implements ErrorMessage {
-    private OnClickButtons onclickButtons;
+    private GetWeather mGetWeather;
 
     @Override
     protected void onCreate(final Bundle savedInstanceState) {
@@ -41,7 +41,7 @@ public class WeatherInformationActivity extends Activity implements ErrorMessage
         final WeatherInformationDataFragment weatherDataFragment = (WeatherInformationDataFragment) this
                 .getFragmentManager().findFragmentById(R.id.weather_data_frag);
 
-        this.onclickButtons = weatherDataFragment;
+        this.mGetWeather = weatherDataFragment;
 
     }
 
@@ -59,15 +59,23 @@ public class WeatherInformationActivity extends Activity implements ErrorMessage
         // automatically handle clicks on the Home/Up button, so long
         // as you specify a parent activity in AndroidManifest.xml.
         super.onOptionsItemSelected(item);
+
+        Intent intent;
         switch (item.getItemId()) {
         case R.id.weather_menu_settings:
-            final Intent intent = new Intent("de.example.exampletdd.WEATHERINFO").
+            intent = new Intent("de.example.exampletdd.WEATHERINFO").
             setComponent(new ComponentName("de.example.exampletdd",
                     "de.example.exampletdd.WeatherInformationPreferencesActivity"));
             this.startActivity(intent);
             return true;
         case R.id.weather_menu_get:
-            this.onClickGetWeather();
+            this.getWeather();
+            return true;
+        case R.id.weather_menu_map:
+            intent = new Intent("de.example.exampletdd.WEATHERINFO")
+            .setComponent(new ComponentName("de.example.exampletdd",
+                    "de.example.exampletdd.WeatherInformationMapActivity"));
+            this.startActivity(intent);
             return true;
         default:
             break;
@@ -83,8 +91,8 @@ public class WeatherInformationActivity extends Activity implements ErrorMessage
         newFragment.show(this.getFragmentManager(), "errorDialog");
     }
 
-    public void onClickGetWeather() {
-        this.onclickButtons.onClickGetWeather();
+    public void getWeather() {
+        this.mGetWeather.getWeather();
     }
 
 }
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationMapActivity.java b/Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationMapActivity.java
new file mode 100644 (file)
index 0000000..ef3bcca
--- /dev/null
@@ -0,0 +1,178 @@
+package de.example.exampletdd;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.text.DecimalFormat;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
+import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
+import com.google.android.gms.maps.MapFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+import de.example.exampletdd.fragment.ErrorDialogFragment;
+import de.example.exampletdd.model.GeocodingData;
+
+public class WeatherInformationMapActivity extends Activity {
+    private static final String WEATHER_GEOCODING_FILE = "weathergeocoding.file";
+    private static final String TAG = "WeatherInformationMapActivity";
+    private GoogleMap mMap;
+    private Marker mMarker;
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        this.setContentView(R.layout.weather_map);
+
+        final MapFragment mapFragment = (MapFragment) this.getFragmentManager()
+                .findFragmentById(R.id.map);
+
+        this.mMap = mapFragment.getMap();
+        this.mMap.setMyLocationEnabled(true);
+        this.mMap.getUiSettings().setCompassEnabled(false);
+        this.mMap.setOnMapLongClickListener(new LongClickListener());
+        this.mMap.setOnMarkerClickListener(new MarkerClickListener());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        GeocodingData geocodingData = null;
+        try {
+            geocodingData = this.restoreGeocodingDataFromFile();
+        } catch (final StreamCorruptedException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        } catch (final FileNotFoundException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        } catch (final IOException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        } catch (final ClassNotFoundException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        }
+
+        if (geocodingData != null) {
+            final LatLng point = new LatLng(
+                    geocodingData.getLatitude(), geocodingData.getLongitude());
+            this.mMap.clear();
+            this.mMarker = this.mMap.addMarker(new MarkerOptions().position(
+                    point).draggable(
+                            true));
+
+            final DecimalFormat tempFormatter = new DecimalFormat("#####.#####");
+            final TextView cityCountry = (TextView) WeatherInformationMapActivity.this
+                    .findViewById(R.id.weather_map_citycountry_data);
+            final String latitude = tempFormatter.format(point.latitude);
+            final String longitude = tempFormatter.format(point.longitude);
+            cityCountry.setText(latitude + "," + longitude);
+        }
+    }
+
+    public void setChosenLocation(final View view) {
+
+        if (this.mMarker == null) {
+            return;
+        }
+
+        final LatLng coordinates = this.mMarker.getPosition();
+
+        final GeocodingData geocodingData = new GeocodingData.Builder()
+        .setLatitude(coordinates.latitude)
+        .setLongitude(coordinates.longitude).build();
+
+        try {
+            this.storeGeocodingDataToFile(geocodingData);
+        } catch (final IOException e) {
+            Log.e(TAG, "Store geocoding data exception: ", e);
+            this.createErrorDialog(R.string.error_dialog_store_geocoding_data);
+        }
+
+    }
+
+    public void createErrorDialog(final int title) {
+        final DialogFragment newFragment = ErrorDialogFragment
+                .newInstance(title);
+        newFragment.show(this.getFragmentManager(), "errorDialog");
+    }
+
+    private void storeGeocodingDataToFile(final GeocodingData geocodingData)
+            throws FileNotFoundException, IOException {
+        final OutputStream persistenceFile = this.openFileOutput(
+                WEATHER_GEOCODING_FILE, Context.MODE_PRIVATE);
+
+        ObjectOutputStream oos = null;
+        try {
+            oos = new ObjectOutputStream(persistenceFile);
+
+            oos.writeObject(geocodingData);
+        } finally {
+            if (oos != null) {
+                oos.close();
+            }
+        }
+    }
+
+    private GeocodingData restoreGeocodingDataFromFile()
+            throws StreamCorruptedException, FileNotFoundException,
+            IOException, ClassNotFoundException {
+        final InputStream persistenceFile = this
+                .openFileInput(WEATHER_GEOCODING_FILE);
+
+        ObjectInputStream ois = null;
+        try {
+            ois = new ObjectInputStream(persistenceFile);
+
+            return (GeocodingData) ois.readObject();
+        } finally {
+            if (ois != null) {
+                ois.close();
+            }
+        }
+    }
+
+    private class LongClickListener implements OnMapLongClickListener {
+
+        @Override
+        public void onMapLongClick(final LatLng point) {
+            if (WeatherInformationMapActivity.this.mMarker == null) {
+                WeatherInformationMapActivity.this.mMarker = WeatherInformationMapActivity.this.mMap
+                        .addMarker(new MarkerOptions().position(point).draggable(
+                                true));
+            } else {
+                WeatherInformationMapActivity.this.mMarker.setPosition(point);
+            }
+
+            final DecimalFormat tempFormatter = new DecimalFormat("#####.#####");
+            final TextView cityCountry = (TextView) WeatherInformationMapActivity.this
+                    .findViewById(R.id.weather_map_citycountry_data);
+            final String latitude = tempFormatter.format(point.latitude);
+            final String longitude = tempFormatter.format(point.longitude);
+            cityCountry.setText(latitude + "," + longitude);
+        }
+    }
+
+    private class MarkerClickListener implements OnMarkerClickListener {
+
+        @Override
+        public boolean onMarkerClick(final Marker marker) {
+            marker.getPosition();
+            return false;
+        }
+
+    }
+}
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/activityinterface/GetWeather.java b/Android/WeatherInformation/src/de/example/exampletdd/activityinterface/GetWeather.java
new file mode 100644 (file)
index 0000000..98734cd
--- /dev/null
@@ -0,0 +1,8 @@
+package de.example.exampletdd.activityinterface;
+
+
+public interface GetWeather {
+
+    public void getWeather();
+
+}
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/activityinterface/OnClickButtons.java b/Android/WeatherInformation/src/de/example/exampletdd/activityinterface/OnClickButtons.java
deleted file mode 100644 (file)
index 2206cc5..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-package de.example.exampletdd.activityinterface;
-
-
-public interface OnClickButtons {
-
-    public void onClickGetWeather();
-
-}
index 32eb712..f7398bc 100644 (file)
@@ -37,18 +37,20 @@ import android.widget.ImageView;
 import android.widget.ListView;
 import de.example.exampletdd.R;
 import de.example.exampletdd.activityinterface.ErrorMessage;
-import de.example.exampletdd.activityinterface.OnClickButtons;
+import de.example.exampletdd.activityinterface.GetWeather;
 import de.example.exampletdd.httpclient.WeatherHTTPClient;
+import de.example.exampletdd.model.GeocodingData;
 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 WeatherInformationDataFragment extends Fragment implements OnClickButtons {
-    private boolean isFahrenheit;
-    private String language;
+public class WeatherInformationDataFragment extends Fragment implements GetWeather {
     private static final String WEATHER_DATA_FILE = "weatherdata.file";
+    private static final String WEATHER_GEOCODING_FILE = "weathergeocoding.file";
     private static final String TAG = "WeatherInformationDataFragment";
+    private boolean mIsFahrenheit;
+    private String mLanguage;
 
     @Override
     public void onCreate(final Bundle savedInstanceState) {
@@ -60,7 +62,7 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
                 .getDefaultSharedPreferences(this.getActivity());
         final String keyPreference = this.getResources().getString(
                 R.string.weather_preferences_language_key);
-        this.language = sharedPreferences.getString(
+        this.mLanguage = sharedPreferences.getString(
                 keyPreference, "");
     }
 
@@ -90,26 +92,41 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
     }
 
     @Override
-    public void onClickGetWeather() {
+    public void getWeather() {
 
-        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);
+        GeocodingData geocodingData = null;
+        try {
+            geocodingData = this.restoreGeocodingDataFromFile();
+        } catch (final StreamCorruptedException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        } catch (final FileNotFoundException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        } catch (final IOException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        } catch (final ClassNotFoundException e) {
+            Log.e(TAG, "onResume exception: ", e);
+        }
+
+        if (geocodingData != null) {
+            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 WeatherTask weatherTask = new WeatherTask(HTTPweatherClient, weatherService);
 
 
-        weatherTask.execute("Candeleda,spain");
+            weatherTask.execute(geocodingData);
+        }
     }
 
     public void updateWeatherData(final WeatherData weatherData) {
         final DecimalFormat tempFormatter = new DecimalFormat("#####.#####");
         final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z");
-        final double tempUnits = this.isFahrenheit ? 0 : 273.15;
+        final double tempUnits = this.mIsFahrenheit ? 0 : 273.15;
 
         final List<WeatherDataEntry> entries = this.createEmptyEntriesList();
 
@@ -178,25 +195,25 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
         final String celsius = this.getResources().getString(
                 R.string.weather_preferences_units_celsius);
         if (unitsPreferenceValue.equals(celsius)) {
-            this.isFahrenheit = false;
+            this.mIsFahrenheit = false;
         } else {
-            this.isFahrenheit = true;
+            this.mIsFahrenheit = true;
         }
 
         keyPreference = this.getResources().getString(
                 R.string.weather_preferences_language_key);
         final String languagePreferenceValue = sharedPreferences.getString(
                 keyPreference, "");
-        if (!languagePreferenceValue.equals(this.language)) {
-            this.language = languagePreferenceValue;
-            this.onClickGetWeather();
+        if (!languagePreferenceValue.equals(this.mLanguage)) {
+            this.mLanguage = languagePreferenceValue;
+            this.getWeather();
 
             return;
         }
 
         WeatherData weatherData = null;
         try {
-            weatherData = this.restoreDataFromFile();
+            weatherData = this.restoreWeatherDataFromFile();
         } catch (final StreamCorruptedException e) {
             Log.e(TAG, "onResume exception: ", e);
         } catch (final FileNotFoundException e) {
@@ -213,7 +230,7 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
     }
 
     public class WeatherTask extends AsyncTask<Object, Void, WeatherData> {
-        private static final String TAG = "JSONWeatherTask";
+        private static final String TAG = "WeatherTask";
         private final WeatherHTTPClient weatherHTTPClient;
         private final WeatherService weatherService;
 
@@ -286,19 +303,19 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
                             R.string.weather_preferences_language_key);
             final String languagePreferenceValue = sharedPreferences.getString(keyPreference, "");
 
-            final String cityCountry = (String) params[0];
-            final String urlAPICity = WeatherInformationDataFragment.this.getResources()
-                    .getString(R.string.uri_api_city);
+            final GeocodingData geocodingData = (GeocodingData) params[0];
+            final String urlAPICoord = WeatherInformationDataFragment.this.getResources()
+                    .getString(R.string.uri_api_coord);
             final String APIVersion = WeatherInformationDataFragment.this.getResources()
                     .getString(R.string.api_version);
-            String url = this.weatherService.createURIAPICityCountry(
-                    cityCountry, urlAPICity, APIVersion, languagePreferenceValue);
+            String url = this.weatherService.createURIAPICoord(geocodingData.getLatitude(),
+                    geocodingData.getLongitude(), urlAPICoord, APIVersion, languagePreferenceValue);
 
 
             final String jsonData = this.weatherHTTPClient.retrieveJSONDataFromAPI(new URL(url));
 
 
-            final WeatherData weatherData = this.weatherService.retrieveWeather(jsonData);
+            final WeatherData weatherData = this.weatherService.retrieveDataFromJPOS(jsonData);
 
 
             final String icon = weatherData.getWeather().getIcon();
@@ -315,7 +332,7 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
 
         private void onPostExecuteThrowable(final WeatherData weatherData)
                 throws FileNotFoundException, IOException {
-            WeatherInformationDataFragment.this.storeWeatherData(weatherData);
+            WeatherInformationDataFragment.this.storeWeatherDataToFile(weatherData);
 
             WeatherInformationDataFragment.this.updateWeatherData(weatherData);
         }
@@ -338,7 +355,7 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
         return entries;
     }
 
-    private void storeWeatherData(final WeatherData weatherData)
+    private void storeWeatherDataToFile(final WeatherData weatherData)
             throws FileNotFoundException, IOException {
         final OutputStream persistenceFile = this.getActivity().openFileOutput(
                 WEATHER_DATA_FILE, Context.MODE_PRIVATE);
@@ -355,7 +372,7 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
         }
     }
 
-    private WeatherData restoreDataFromFile() throws StreamCorruptedException,
+    private WeatherData restoreWeatherDataFromFile() throws StreamCorruptedException,
     FileNotFoundException, IOException, ClassNotFoundException {
         final InputStream persistenceFile = this.getActivity().openFileInput(
                 WEATHER_DATA_FILE);
@@ -371,4 +388,22 @@ public class WeatherInformationDataFragment extends Fragment implements OnClickB
             }
         }
     }
+
+    private GeocodingData restoreGeocodingDataFromFile()
+            throws StreamCorruptedException, FileNotFoundException,
+            IOException, ClassNotFoundException {
+        final InputStream persistenceFile = this.getActivity()
+                .openFileInput(WEATHER_GEOCODING_FILE);
+
+        ObjectInputStream ois = null;
+        try {
+            ois = new ObjectInputStream(persistenceFile);
+
+            return (GeocodingData) ois.readObject();
+        } finally {
+            if (ois != null) {
+                ois.close();
+            }
+        }
+    }
 }
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/model/GeocodingData.java b/Android/WeatherInformation/src/de/example/exampletdd/model/GeocodingData.java
new file mode 100644 (file)
index 0000000..d61046f
--- /dev/null
@@ -0,0 +1,109 @@
+package de.example.exampletdd.model;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+
+public class GeocodingData implements Serializable {
+    private static final long serialVersionUID = -6774793966351174343L;
+    private final String city;
+    private final String country;
+    private final GeocodingStatus status;
+    private final double latitude;
+    private final double longitude;
+
+    public static class Builder {
+        private String mCity;
+        private String mCountry;
+        private GeocodingStatus mStatus;
+        private double mLatitude;
+        private double mLongitude;
+
+        public Builder setCity(final String city) {
+            this.mCity = city;
+            return this;
+        }
+
+        public Builder setCountry(final String country) {
+            this.mCountry = country;
+            return this;
+        }
+
+        public Builder setStatus(final GeocodingStatus status) {
+            this.mStatus = status;
+            return this;
+        }
+
+        public Builder setLatitude(final double latitude) {
+            this.mLatitude = latitude;
+            return this;
+        }
+
+        public Builder setLongitude(final double longitude) {
+            this.mLongitude = longitude;
+            return this;
+        }
+
+        public GeocodingData build() {
+            return new GeocodingData(this);
+        }
+    }
+
+    private GeocodingData(final Builder builder) {
+        super();
+        this.city = builder.mCity;
+        this.country = builder.mCountry;
+        this.status = builder.mStatus;
+        this.latitude = builder.mLatitude;
+        this.longitude = builder.mLongitude;
+    }
+
+    public String getCity() {
+        return this.city;
+    }
+
+    public String getCountry() {
+        return this.country;
+    }
+
+    public GeocodingStatus getStatus() {
+        return this.status;
+    }
+
+    public double getLatitude() {
+        return this.latitude;
+    }
+
+    public double getLongitude() {
+        return this.longitude;
+    }
+
+    public enum GeocodingStatus {
+        // see: https://developers.google.com/maps/documentation/geocoding/
+        OK("OK"),
+        ZERO_RESULTS("ZERO_RESULTS"),
+        OVER_QUERY_LIMIT("OVER_QUERY_LIMIT"),
+        REQUEST_DENIED("REQUEST_DENIED"),
+        INVALID_REQUEST("INVALID_REQUEST"),
+        UNKNOWN_ERROR("UNKNOWN_ERROR");
+
+        private final String codeValue;
+        private static final HashMap<String, GeocodingStatus> mapStatus =
+                new HashMap<String, GeocodingStatus>();
+
+        static {
+            for (final GeocodingStatus status : GeocodingStatus.values()) {
+                mapStatus.put(status.codeValue, status);
+            }
+        }
+
+        private GeocodingStatus(final String codeValue) {
+            this.codeValue = codeValue;
+        }
+
+        public static GeocodingStatus getGeocodingStatus(final String value) {
+            return mapStatus.get(value);
+        }
+
+    }
+}
index c351010..184a6c7 100644 (file)
@@ -7,4 +7,5 @@ import de.example.exampletdd.model.WeatherData;
 public interface IJPOSWeatherParser {
 
     WeatherData retrieveWeatherFromJPOS(final String jsonData) throws JSONException;
+
 }
index f92a2ef..7c2f6ff 100644 (file)
@@ -39,14 +39,9 @@ public class JPOSWeatherParser implements IJPOSWeatherParser {
 
         jsonObject = jsonWeatherData.getJSONObject("main");
         final double temp = jsonObject.getDouble("temp");
-        double minTemp = 0;
-        try {
-            minTemp = jsonObject.getDouble("temp_min");
-        } catch (final JSONException e) {}
-        double maxTemp = 0;
-        try {
-            maxTemp = jsonObject.getDouble("temp_max");
-        } catch (final JSONException e) {}
+        final double minTemp = jsonObject.optDouble("temp_min", 0);
+
+        final double maxTemp = jsonObject.optDouble("temp_max", 0);
         final double humidity = jsonObject.getDouble("humidity");
         final double pressure = jsonObject.getDouble("pressure");
         final WeatherData.Main main = new WeatherData.Main(temp, minTemp,
@@ -55,18 +50,9 @@ public class JPOSWeatherParser implements IJPOSWeatherParser {
         jsonObject = jsonWeatherData.getJSONObject("wind");
         final double speed = jsonObject.getDouble("speed");
         final double deg = jsonObject.getDouble("deg");
-        double gust = 0;
-        try {
-            gust = jsonObject.getDouble("gust");
-        } catch (final JSONException e) {}
-        double var_beg = 0;
-        try {
-            var_beg = jsonObject.getDouble("var_beg");
-        } catch (final JSONException e) {}
-        double var_end = 0;
-        try {
-            var_end = jsonObject.getDouble("var_end");
-        } catch (final JSONException e) {}
+        final double gust = jsonObject.optDouble("gust", 0);
+        final double var_beg = jsonObject.optDouble("var_beg", 0);
+        final double var_end = jsonObject.optDouble("var_end", 0);
         final WeatherData.Wind wind = new WeatherData.Wind(speed, deg, gust,
                 var_beg, var_end);
 
@@ -74,10 +60,7 @@ public class JPOSWeatherParser implements IJPOSWeatherParser {
         final double cloudiness = jsonObject.getDouble("all");
         final WeatherData.Clouds clouds = new WeatherData.Clouds(cloudiness);
 
-        double time = 0;
-        try {
-            time = jsonObject.getDouble("time");
-        } catch (final JSONException e) {}
+        final double time = jsonObject.optDouble("time", 0);
         final WeatherData.DataReceivingTime dataReceivingTime =
                 new WeatherData.DataReceivingTime(time);
 
@@ -87,4 +70,5 @@ public class JPOSWeatherParser implements IJPOSWeatherParser {
                 .setClouds(clouds).setDataReceivingTime(dataReceivingTime)
                 .build();
     }
+
 }
index 356288b..2d9c363 100644 (file)
@@ -15,19 +15,21 @@ public class WeatherService {
         this.JPOSWeatherParser = JPOSWeatherParser;
     }
 
-    public WeatherData retrieveWeather(final String jsonData) throws JSONException {
+    public WeatherData retrieveDataFromJPOS(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) {
+    public String createURIAPICoord(final double latitude,
+            final double longitude, final String urlAPI,
+            final String APIVersion, final String language) {
 
         final MessageFormat formatURIAPI = new MessageFormat(urlAPI,
                 Locale.ENGLISH);
-        final Object[] values = new Object[3];
+        final Object[] values = new Object[4];
         values[0] = APIVersion;
         values[1] = latitude;
         values[2] = longitude;
+        values[3] = language;
 
         return formatURIAPI.format(values);
     }