WeatherInformation:
authorgu.martinm@gmail.com <gu.martinm@gmail.com>
Sun, 28 Sep 2014 01:03:31 +0000 (03:03 +0200)
committergu.martinm@gmail.com <gu.martinm@gmail.com>
Sun, 28 Sep 2014 01:03:31 +0000 (03:03 +0200)
1. Save forecast and current data to permanent storage.
2. Update widget from App when location changed (no working yet)

13 files changed:
Android/WeatherInformation/AndroidManifest.xml
Android/WeatherInformation/res/xml/appwidget_provider.xml
Android/WeatherInformation/src/de/example/exampletdd/NotificationIntentService.java [new file with mode: 0644]
Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationApplication.java [deleted file]
Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationBatch.java [deleted file]
Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationBootReceiver.java
Android/WeatherInformation/src/de/example/exampletdd/WidgetIntentService.java [new file with mode: 0644]
Android/WeatherInformation/src/de/example/exampletdd/fragment/current/CurrentFragment.java
Android/WeatherInformation/src/de/example/exampletdd/fragment/overview/OverviewFragment.java
Android/WeatherInformation/src/de/example/exampletdd/fragment/preferences/WeatherInformationPreferencesFragment.java
Android/WeatherInformation/src/de/example/exampletdd/fragment/specific/SpecificFragment.java
Android/WeatherInformation/src/de/example/exampletdd/service/PermanentStorage.java [new file with mode: 0644]
Android/WeatherInformation/src/de/example/exampletdd/widget/WeatherInformationWidgetProvider.java

index 8cfc5c5..e49f556 100644 (file)
@@ -34,7 +34,7 @@
     
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> 
     
-    <application android:name="de.example.exampletdd.WeatherInformationApplication"
+    <application
         android:allowBackup="true"
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name"
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".widget.WeatherInformationWidgetConfigure">
+        <activity android:name="de.example.exampletdd.widget.WeatherInformationWidgetConfigure">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
             </intent-filter>
         </activity>
         
-        <receiver android:name=".WeatherInformationBootReceiver"
+        <receiver android:name="de.example.exampletdd.WeatherInformationBootReceiver"
             android:enabled="true">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED"></action>
         </receiver>
 
         <!-- Service to update Notification -->
-        <service android:name=".WeatherInformationBatch"
+        <service android:name="de.example.exampletdd.NotificationIntentService"
             android:exported="false"
             android:enabled="true" />
         
         <!-- Service to update Widget -->
-               <service android:name=".WeatherInformationWidgetProvider$UpdateService"
+               <service android:name="de.example.exampletdd.WidgetIntentService"
                    android:exported="false"
             android:enabled="true" />
         
index 396c70a..74390c6 100644 (file)
@@ -9,5 +9,5 @@
     android:initialLayout="@layout/appwidget"
     android:configure="de.example.exampletdd.widget.WeatherInformationWidgetConfigure" 
     android:widgetCategory="home_screen|keyguard"
-    android:updatePeriodMillis="900000">
+    android:updatePeriodMillis="3600000">
 </appwidget-provider>
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/NotificationIntentService.java b/Android/WeatherInformation/src/de/example/exampletdd/NotificationIntentService.java
new file mode 100644 (file)
index 0000000..f953b53
--- /dev/null
@@ -0,0 +1,204 @@
+package de.example.exampletdd;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+import org.apache.http.client.ClientProtocolException;
+
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.http.AndroidHttpClient;
+import android.preference.PreferenceManager;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.TaskStackBuilder;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import com.fasterxml.jackson.core.JsonParseException;
+
+import de.example.exampletdd.httpclient.CustomHTTPClient;
+import de.example.exampletdd.model.DatabaseQueries;
+import de.example.exampletdd.model.WeatherLocation;
+import de.example.exampletdd.model.currentweather.Current;
+import de.example.exampletdd.parser.JPOSWeatherParser;
+import de.example.exampletdd.service.IconsList;
+import de.example.exampletdd.service.ServiceParser;
+
+public class NotificationIntentService extends IntentService {
+    private static final String TAG = "NotificationIntentService";
+
+
+    public NotificationIntentService() {
+        super("NIS-Thread");
+    }
+
+    @Override
+    protected void onHandleIntent(final Intent intent) {
+        final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
+        final WeatherLocation weatherLocation = query.queryDataBase();
+        
+        if (weatherLocation != null) {
+            final ServiceParser weatherService = new ServiceParser(new JPOSWeatherParser());
+            final CustomHTTPClient HTTPClient = new CustomHTTPClient(
+                    AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent"));
+
+            Current current = null;
+            try {
+               current = this.doInBackgroundThrowable(weatherLocation, HTTPClient, weatherService);
+                
+            } catch (final JsonParseException e) {
+                Log.e(TAG, "doInBackground exception: ", e);
+            } catch (final ClientProtocolException e) {
+                Log.e(TAG, "doInBackground exception: ", e);
+            } catch (final MalformedURLException e) {
+                Log.e(TAG, "doInBackground exception: ", e);
+            } catch (final URISyntaxException e) {
+                Log.e(TAG, "doInBackground exception: ", e);
+            } catch (final IOException e) {
+                // logger infrastructure swallows UnknownHostException :/
+                Log.e(TAG, "doInBackground exception: " + e.getMessage(), e);
+            } finally {
+                HTTPClient.close();
+            }
+            
+            if (current != null) {
+               this.showNotification(current, weatherLocation);
+            }
+        }
+    }
+
+    private Current doInBackgroundThrowable(final WeatherLocation weatherLocation,
+            final CustomHTTPClient HTTPClient, final ServiceParser weatherService)
+                    throws ClientProtocolException, MalformedURLException, URISyntaxException,
+                    JsonParseException, IOException {
+
+        final String APIVersion = this.getResources().getString(R.string.api_version);
+
+        final String urlAPI = this.getResources().getString(R.string.uri_api_weather_today);
+        final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion,
+                weatherLocation.getLatitude(), weatherLocation.getLongitude());
+        final String jsonData = HTTPClient.retrieveDataAsString(new URL(url));
+        final Current current = weatherService.retrieveCurrentFromJPOS(jsonData);
+        // TODO: what is this for? I guess I could skip it :/
+        final Calendar now = Calendar.getInstance();
+        current.setDate(now.getTime());
+        
+        return current;
+    }
+    
+    private void showNotification(final Current current, final WeatherLocation weatherLocation) {
+        final SharedPreferences sharedPreferences = PreferenceManager
+                .getDefaultSharedPreferences(this.getApplicationContext());
+
+        // TODO: repeating the same code in Overview, Specific and Current!!!
+        // 1. Update units of measurement.
+        boolean mIsFahrenheit = false;
+        final String keyPreference = this.getResources().getString(
+                R.string.weather_preferences_units_key);
+        final String unitsPreferenceValue = sharedPreferences.getString(keyPreference, "");
+        final String celsius = this.getResources().getString(
+                R.string.weather_preferences_units_celsius);
+        if (unitsPreferenceValue.equals(celsius)) {
+            mIsFahrenheit = false;
+        } else {
+            mIsFahrenheit = true;
+        }
+        final double tempUnits = mIsFahrenheit ? 0 : 273.15;
+        final String symbol = mIsFahrenheit ? "ºF" : "ºC";
+
+
+        // 2. Formatters
+        final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
+        tempFormatter.applyPattern("#####.#####");
+
+
+        // 3. Prepare data for RemoteViews.
+        String tempMax = "";
+        if (current.getMain().getTemp_max() != null) {
+            double conversion = (Double) current.getMain().getTemp_max();
+            conversion = conversion - tempUnits;
+            tempMax = tempFormatter.format(conversion) + symbol;
+        }
+        String tempMin = "";
+        if (current.getMain().getTemp_min() != null) {
+            double conversion = (Double) current.getMain().getTemp_min();
+            conversion = conversion - tempUnits;
+            tempMin = tempFormatter.format(conversion) + symbol;
+        }
+        Bitmap picture;
+        if ((current.getWeather().size() > 0)
+                && (current.getWeather().get(0).getIcon() != null)
+                && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
+            final String icon = current.getWeather().get(0).getIcon();
+            picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
+                    .getResourceDrawable());
+        } else {
+            picture = BitmapFactory.decodeResource(this.getResources(),
+                    R.drawable.weather_severe_alert);
+        }
+        final String city = weatherLocation.getCity();
+        final String country = weatherLocation.getCountry();
+        
+        // 4. Insert data in RemoteViews.
+        final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.notification);
+        remoteView.setImageViewBitmap(R.id.weather_notification_image, picture);
+        remoteView.setTextViewText(R.id.weather_notification_temperature_max, tempMax);
+        remoteView.setTextViewText(R.id.weather_notification_temperature_min, tempMin);
+        remoteView.setTextViewText(R.id.weather_notification_city, city);
+        remoteView.setTextViewText(R.id.weather_notification_country, country);
+
+        // 5. Activity launcher.
+        final Intent resultIntent =  new Intent(this.getApplicationContext(), WeatherTabsActivity.class);
+        // The PendingIntent to launch our activity if the user selects this notification
+//        final PendingIntent contentIntent = PendingIntent.getActivity(
+//                     this.getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+        // The stack builder object will contain an artificial back stack for the started Activity.
+        // This ensures that navigating backward from the Activity leads out of
+        // your application to the Home screen.
+        final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
+        // Adds the back stack for the Intent (but not the Intent itself)
+        stackBuilder.addParentStack(WeatherTabsActivity.class);
+        // Adds the Intent that starts the Activity to the top of the stack
+        stackBuilder.addNextIntent(resultIntent);
+        final PendingIntent resultPendingIntent =
+                       stackBuilder.getPendingIntent(
+                    0,
+                    PendingIntent.FLAG_UPDATE_CURRENT
+                );
+        
+       final NotificationManagerCompat notificationManager =
+                       NotificationManagerCompat.from(this.getApplicationContext());
+       
+
+       // 6. Create notification.
+        final NotificationCompat.Builder notificationBuilder =
+                       new NotificationCompat.Builder(this.getApplicationContext())
+                       .setContent(remoteView)
+                .setSmallIcon(R.drawable.ic_launcher)
+                .setAutoCancel(true)
+                .setLocalOnly(true)
+                .setWhen(System.currentTimeMillis())
+                .setContentIntent(resultPendingIntent)
+                .setPriority(NotificationCompat.PRIORITY_DEFAULT);
+        
+        final Notification notification = notificationBuilder.build();
+        notification.flags |= Notification.FLAG_AUTO_CANCEL;
+
+        // Send the notification.
+        // Sets an ID for the notification, so it can be updated (just in case)
+        int notifyID = 1;
+        notificationManager.notify(notifyID, notification);
+    }
+}
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationApplication.java b/Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationApplication.java
deleted file mode 100644 (file)
index 679a2ca..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-package de.example.exampletdd;
-
-import android.app.Application;
-import de.example.exampletdd.model.currentweather.Current;
-import de.example.exampletdd.model.forecastweather.Forecast;
-
-public class WeatherInformationApplication extends Application {
-    private Forecast mForecast;
-    private Current mCurrent;
-    private String mCity;
-    private String mCountry;
-
-
-    public void setForecast(final Forecast forecast) {
-        this.mForecast = forecast;
-    }
-
-    public Forecast getForecast() {
-        return this.mForecast;
-    }
-    
-    public void setCurrent(final Current current) {
-       this.mCurrent = current;
-    }
-    
-    public Current getCurrent() {
-       return this.mCurrent;
-    }
-    
-    public void setCity(final String city) {
-       this.mCity = city;
-    }
-    
-    public String getCity() {
-       return this.mCity;
-    }
-    
-    public void setCountry(final String country) {
-       this.mCountry = country;
-    }
-    
-    public String getCountry() {
-       return this.mCountry;
-    }
-} 
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationBatch.java b/Android/WeatherInformation/src/de/example/exampletdd/WeatherInformationBatch.java
deleted file mode 100644 (file)
index afe42ab..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-package de.example.exampletdd;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.Calendar;
-import java.util.Locale;
-
-import org.apache.http.client.ClientProtocolException;
-
-import android.app.IntentService;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.http.AndroidHttpClient;
-import android.preference.PreferenceManager;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.NotificationManagerCompat;
-import android.support.v4.app.TaskStackBuilder;
-import android.util.Log;
-import android.widget.RemoteViews;
-
-import com.fasterxml.jackson.core.JsonParseException;
-
-import de.example.exampletdd.httpclient.CustomHTTPClient;
-import de.example.exampletdd.model.DatabaseQueries;
-import de.example.exampletdd.model.WeatherLocation;
-import de.example.exampletdd.model.currentweather.Current;
-import de.example.exampletdd.parser.JPOSWeatherParser;
-import de.example.exampletdd.service.IconsList;
-import de.example.exampletdd.service.ServiceParser;
-
-public class WeatherInformationBatch extends IntentService {
-    private static final String TAG = "WeatherInformationBatch";
-
-
-    public WeatherInformationBatch() {
-        super("WIB-Thread");
-    }
-
-    @Override
-    protected void onHandleIntent(final Intent intent) {
-        final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
-        final WeatherLocation weatherLocation = query.queryDataBase();
-        
-        if (weatherLocation != null) {
-            final ServiceParser weatherService = new ServiceParser(new JPOSWeatherParser());
-            final CustomHTTPClient HTTPClient = new CustomHTTPClient(
-                    AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent"));
-
-            Current current = null;
-            try {
-               current = this.doInBackgroundThrowable(weatherLocation, HTTPClient, weatherService);
-                
-            } catch (final JsonParseException e) {
-                Log.e(TAG, "doInBackground exception: ", e);
-            } catch (final ClientProtocolException e) {
-                Log.e(TAG, "doInBackground exception: ", e);
-            } catch (final MalformedURLException e) {
-                Log.e(TAG, "doInBackground exception: ", e);
-            } catch (final URISyntaxException e) {
-                Log.e(TAG, "doInBackground exception: ", e);
-            } catch (final IOException e) {
-                // logger infrastructure swallows UnknownHostException :/
-                Log.e(TAG, "doInBackground exception: " + e.getMessage(), e);
-            } finally {
-                HTTPClient.close();
-            }
-            
-            if (current != null) {
-               this.showNotification(current, weatherLocation);
-            }
-        }
-    }
-
-    private Current doInBackgroundThrowable(final WeatherLocation weatherLocation,
-            final CustomHTTPClient HTTPClient, final ServiceParser weatherService)
-                    throws ClientProtocolException, MalformedURLException, URISyntaxException,
-                    JsonParseException, IOException {
-
-        final String APIVersion = this.getResources().getString(R.string.api_version);
-
-        final String urlAPI = this.getResources().getString(R.string.uri_api_weather_today);
-        final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion,
-                weatherLocation.getLatitude(), weatherLocation.getLongitude());
-        final String jsonData = HTTPClient.retrieveDataAsString(new URL(url));
-        final Current current = weatherService.retrieveCurrentFromJPOS(jsonData);
-        // TODO: what is this for? I guess I could skip it :/
-        final Calendar now = Calendar.getInstance();
-        current.setDate(now.getTime());
-        
-        return current;
-    }
-    
-    private void showNotification(final Current current, final WeatherLocation weatherLocation) {
-        final SharedPreferences sharedPreferences = PreferenceManager
-                .getDefaultSharedPreferences(this.getApplicationContext());
-
-        // TODO: repeating the same code in Overview, Specific and Current!!!
-        // 1. Update units of measurement.
-        boolean mIsFahrenheit = false;
-        final String keyPreference = this.getResources().getString(
-                R.string.weather_preferences_units_key);
-        final String unitsPreferenceValue = sharedPreferences.getString(keyPreference, "");
-        final String celsius = this.getResources().getString(
-                R.string.weather_preferences_units_celsius);
-        if (unitsPreferenceValue.equals(celsius)) {
-            mIsFahrenheit = false;
-        } else {
-            mIsFahrenheit = true;
-        }
-        final double tempUnits = mIsFahrenheit ? 0 : 273.15;
-        final String symbol = mIsFahrenheit ? "ºF" : "ºC";
-
-
-        // 2. Formatters
-        final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
-        tempFormatter.applyPattern("#####.#####");
-
-
-        // 3. Prepare data for RemoteViews.
-        String tempMax = "";
-        if (current.getMain().getTemp_max() != null) {
-            double conversion = (Double) current.getMain().getTemp_max();
-            conversion = conversion - tempUnits;
-            tempMax = tempFormatter.format(conversion) + symbol;
-        }
-        String tempMin = "";
-        if (current.getMain().getTemp_min() != null) {
-            double conversion = (Double) current.getMain().getTemp_min();
-            conversion = conversion - tempUnits;
-            tempMin = tempFormatter.format(conversion) + symbol;
-        }
-        Bitmap picture;
-        if ((current.getWeather().size() > 0)
-                && (current.getWeather().get(0).getIcon() != null)
-                && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
-            final String icon = current.getWeather().get(0).getIcon();
-            picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
-                    .getResourceDrawable());
-        } else {
-            picture = BitmapFactory.decodeResource(this.getResources(),
-                    R.drawable.weather_severe_alert);
-        }
-        final String city = weatherLocation.getCity();
-        final String country = weatherLocation.getCountry();
-        
-        // 4. Insert data in RemoteViews.
-        final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.notification);
-        remoteView.setImageViewBitmap(R.id.weather_notification_image, picture);
-        remoteView.setTextViewText(R.id.weather_notification_temperature_max, tempMax);
-        remoteView.setTextViewText(R.id.weather_notification_temperature_min, tempMin);
-        remoteView.setTextViewText(R.id.weather_notification_city, city);
-        remoteView.setTextViewText(R.id.weather_notification_country, country);
-
-        // 5. Activity launcher.
-        final Intent resultIntent =  new Intent(this.getApplicationContext(), WeatherTabsActivity.class);
-        // The PendingIntent to launch our activity if the user selects this notification
-//        final PendingIntent contentIntent = PendingIntent.getActivity(
-//                     this.getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-        // The stack builder object will contain an artificial back stack for the started Activity.
-        // This ensures that navigating backward from the Activity leads out of
-        // your application to the Home screen.
-        final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
-        // Adds the back stack for the Intent (but not the Intent itself)
-        stackBuilder.addParentStack(WeatherTabsActivity.class);
-        // Adds the Intent that starts the Activity to the top of the stack
-        stackBuilder.addNextIntent(resultIntent);
-        final PendingIntent resultPendingIntent =
-                       stackBuilder.getPendingIntent(
-                    0,
-                    PendingIntent.FLAG_UPDATE_CURRENT
-                );
-        
-       final NotificationManagerCompat notificationManager =
-                       NotificationManagerCompat.from(this.getApplicationContext());
-       
-
-       // 6. Create notification.
-        final NotificationCompat.Builder notificationBuilder =
-                       new NotificationCompat.Builder(this.getApplicationContext())
-                       .setContent(remoteView)
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setAutoCancel(true)
-                .setLocalOnly(true)
-                .setWhen(System.currentTimeMillis())
-                .setContentIntent(resultPendingIntent)
-                .setPriority(NotificationCompat.PRIORITY_DEFAULT);
-        
-        final Notification notification = notificationBuilder.build();
-        notification.flags |= Notification.FLAG_AUTO_CANCEL;
-
-        // Send the notification.
-        // Sets an ID for the notification, so it can be updated (just in case)
-        int notifyID = 1;
-        notificationManager.notify(notifyID, notification);
-    }
-}
index a2a8f14..a9c939a 100644 (file)
@@ -39,7 +39,7 @@ public class WeatherInformationBootReceiver extends BroadcastReceiver {
                 final AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
                 // TODO: better use some string instead of .class? In case I change the service class
                 // this could be a problem (I guess)
-                final Intent serviceIntent = new Intent(context, WeatherInformationBatch.class);
+                final Intent serviceIntent = new Intent(context, NotificationIntentService.class);
                 final PendingIntent alarmIntent = PendingIntent.getService(context, 0, serviceIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT);
                 alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime()
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/WidgetIntentService.java b/Android/WeatherInformation/src/de/example/exampletdd/WidgetIntentService.java
new file mode 100644 (file)
index 0000000..9ee67ac
--- /dev/null
@@ -0,0 +1,239 @@
+package de.example.exampletdd;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+import org.apache.http.client.ClientProtocolException;
+
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.http.AndroidHttpClient;
+import android.preference.PreferenceManager;
+import android.support.v4.app.TaskStackBuilder;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import com.fasterxml.jackson.core.JsonParseException;
+
+import de.example.exampletdd.httpclient.CustomHTTPClient;
+import de.example.exampletdd.model.DatabaseQueries;
+import de.example.exampletdd.model.WeatherLocation;
+import de.example.exampletdd.model.currentweather.Current;
+import de.example.exampletdd.parser.JPOSWeatherParser;
+import de.example.exampletdd.service.IconsList;
+import de.example.exampletdd.service.PermanentStorage;
+import de.example.exampletdd.service.ServiceParser;
+
+public class WidgetIntentService extends IntentService {
+       private static final String TAG = "WidgetIntentService";
+
+
+       public WidgetIntentService() {
+               super("WIS-Thread");
+       }
+
+       @Override
+       protected void onHandleIntent(final Intent intent) {
+               Log.i(TAG, "onHandleIntent");
+               final int appWidgetId = intent.getIntExtra("appWidgetId", -666);
+               final boolean isUpdateByApp = intent.getBooleanExtra("updateByApp", false);
+
+               final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
+               final WeatherLocation weatherLocation = query.queryDataBase();
+               
+               if (weatherLocation == null) {
+                       // Nothing to do.               
+                       return;
+               }
+               
+               if (isUpdateByApp) {            
+                       this.updateByApp(weatherLocation);
+               } else {
+                       this.updateByTimeout(weatherLocation, appWidgetId);
+               }
+
+       }
+
+       private void updateByApp(final WeatherLocation weatherLocation) {
+               final PermanentStorage store = new PermanentStorage(this.getApplicationContext());
+        final Current current = store.getCurrent();
+               final RemoteViews view = this.makeView(current, weatherLocation);
+               
+               this.updateWidgets(view);
+       }
+       
+       private void updateByTimeout(final WeatherLocation weatherLocation, final int appWidgetId) {
+               if (appWidgetId == -666) {
+                       // Nothing to do. Something went wrong.
+                       return;
+               }
+               
+               final Current current = this.getRemoteCurrent(weatherLocation);
+               if (current != null) {
+                       final RemoteViews view = this.makeView(current, weatherLocation);
+                       this.updateWidget(view, appWidgetId);
+               } else {
+                       // TODO: show layout error in Widget
+               }
+       }
+       
+       private Current getRemoteCurrent(final WeatherLocation weatherLocation) {
+
+               final ServiceParser weatherService = new ServiceParser(new JPOSWeatherParser());
+               final CustomHTTPClient HTTPClient = new CustomHTTPClient(
+                               AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent"));
+
+               try {
+                       return this.getRemoteCurrentThrowable(weatherLocation, HTTPClient, weatherService);
+
+               } catch (final JsonParseException e) {
+                       Log.e(TAG, "doInBackground exception: ", e);
+               } catch (final ClientProtocolException e) {
+                       Log.e(TAG, "doInBackground exception: ", e);
+               } catch (final MalformedURLException e) {
+                       Log.e(TAG, "doInBackground exception: ", e);
+               } catch (final URISyntaxException e) {
+                       Log.e(TAG, "doInBackground exception: ", e);
+               } catch (final IOException e) {
+                       // logger infrastructure swallows UnknownHostException :/
+                       Log.e(TAG, "doInBackground exception: " + e.getMessage(), e);
+               } finally {
+                       HTTPClient.close();
+               }
+
+               return null;
+       }
+
+       private Current getRemoteCurrentThrowable(final WeatherLocation weatherLocation,
+                       final CustomHTTPClient HTTPClient, final ServiceParser weatherService)
+                                       throws ClientProtocolException, MalformedURLException, URISyntaxException,
+                                       JsonParseException, IOException {
+
+               final String APIVersion = this.getResources().getString(R.string.api_version);
+
+               final String urlAPI = this.getResources().getString(R.string.uri_api_weather_today);
+               final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion,
+                               weatherLocation.getLatitude(), weatherLocation.getLongitude());
+               final String jsonData = HTTPClient.retrieveDataAsString(new URL(url));
+               final Current current = weatherService.retrieveCurrentFromJPOS(jsonData);
+               // TODO: what is this for? I guess I could skip it :/
+               final Calendar now = Calendar.getInstance();
+               current.setDate(now.getTime());
+
+               return current;
+       }
+
+       private RemoteViews makeView(final Current current, final WeatherLocation weatherLocation) {
+               final SharedPreferences sharedPreferences = PreferenceManager
+                               .getDefaultSharedPreferences(this.getApplicationContext());
+
+               // TODO: repeating the same code in Overview, Specific and Current!!!
+               // 1. Update units of measurement.
+               boolean mIsFahrenheit = false;
+               final String keyPreference = this.getResources().getString(
+                               R.string.weather_preferences_units_key);
+               final String unitsPreferenceValue = sharedPreferences.getString(keyPreference, "");
+               final String celsius = this.getResources().getString(
+                               R.string.weather_preferences_units_celsius);
+               if (unitsPreferenceValue.equals(celsius)) {
+                       mIsFahrenheit = false;
+               } else {
+                       mIsFahrenheit = true;
+               }
+               final double tempUnits = mIsFahrenheit ? 0 : 273.15;
+               final String symbol = mIsFahrenheit ? "ºF" : "ºC";
+
+
+               // 2. Formatters
+               final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
+               tempFormatter.applyPattern("#####.#####");
+
+
+               // 3. Prepare data for RemoteViews.
+               String tempMax = "";
+               if (current.getMain().getTemp_max() != null) {
+                       double conversion = (Double) current.getMain().getTemp_max();
+                       conversion = conversion - tempUnits;
+                       tempMax = tempFormatter.format(conversion) + symbol;
+               }
+               String tempMin = "";
+               if (current.getMain().getTemp_min() != null) {
+                       double conversion = (Double) current.getMain().getTemp_min();
+                       conversion = conversion - tempUnits;
+                       tempMin = tempFormatter.format(conversion) + symbol;
+               }
+               Bitmap picture;
+               if ((current.getWeather().size() > 0)
+                               && (current.getWeather().get(0).getIcon() != null)
+                               && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
+                       final String icon = current.getWeather().get(0).getIcon();
+                       picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
+                                       .getResourceDrawable());
+               } else {
+                       picture = BitmapFactory.decodeResource(this.getResources(),
+                                       R.drawable.weather_severe_alert);
+               }
+               final String city = weatherLocation.getCity();
+               final String country = weatherLocation.getCountry();
+
+               // 4. Insert data in RemoteViews.
+               final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.appwidget);
+               remoteView.setImageViewBitmap(R.id.weather_appwidget_image, picture);
+               remoteView.setTextViewText(R.id.weather_appwidget_temperature_max, tempMax);
+               remoteView.setTextViewText(R.id.weather_appwidget_temperature_min, tempMin);
+               remoteView.setTextViewText(R.id.weather_appwidget_city, city);
+               remoteView.setTextViewText(R.id.weather_appwidget_country, country);
+
+               // 5. Activity launcher.
+               final Intent resultIntent =  new Intent(this.getApplicationContext(), WeatherTabsActivity.class);
+               // The PendingIntent to launch our activity if the user selects this notification
+               //            final PendingIntent contentIntent = PendingIntent.getActivity(
+               //                      this.getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+               // The stack builder object will contain an artificial back stack for the started Activity.
+               // This ensures that navigating backward from the Activity leads out of
+               // your application to the Home screen.
+               final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
+               // Adds the back stack for the Intent (but not the Intent itself)
+               stackBuilder.addParentStack(WeatherTabsActivity.class);
+               // Adds the Intent that starts the Activity to the top of the stack
+               stackBuilder.addNextIntent(resultIntent);
+               final PendingIntent resultPendingIntent =
+                               stackBuilder.getPendingIntent(
+                                               0,
+                                               PendingIntent.FLAG_UPDATE_CURRENT
+                                               );
+               remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
+               
+               return remoteView;
+       }
+       
+       private void updateWidget(final RemoteViews remoteView, final int appWidgetId) {
+               
+               final AppWidgetManager manager = AppWidgetManager.getInstance(this);
+               manager.updateAppWidget(appWidgetId, remoteView);
+
+               Log.i(TAG, "updateWidget updated");
+       }
+       
+       private void updateWidgets(final RemoteViews remoteView) {
+               
+               final ComponentName widgets = new ComponentName("de.example.exampletdd.widget", AppWidgetProvider.class.getCanonicalName());
+               final AppWidgetManager manager = AppWidgetManager.getInstance(this);
+               manager.updateAppWidget(widgets, remoteView);
+
+               Log.i(TAG, "updateWidgets updated");
+       }
+}
index 9b829f8..64d83a2 100644 (file)
@@ -37,13 +37,14 @@ import android.widget.TextView;
 import com.fasterxml.jackson.core.JsonParseException;
 
 import de.example.exampletdd.R;
-import de.example.exampletdd.WeatherInformationApplication;
+import de.example.exampletdd.WidgetIntentService;
 import de.example.exampletdd.httpclient.CustomHTTPClient;
 import de.example.exampletdd.model.DatabaseQueries;
 import de.example.exampletdd.model.WeatherLocation;
 import de.example.exampletdd.model.currentweather.Current;
 import de.example.exampletdd.parser.JPOSWeatherParser;
 import de.example.exampletdd.service.IconsList;
+import de.example.exampletdd.service.PermanentStorage;
 import de.example.exampletdd.service.ServiceParser;
 
 public class CurrentFragment extends Fragment {
@@ -74,9 +75,8 @@ public class CurrentFragment extends Fragment {
             // TODO: Could it be better to store in global forecast data even if it is null value?
             //       So, perhaps do not check for null value and always store in global variable.
             if (current != null) {
-               final WeatherInformationApplication application =
-                               (WeatherInformationApplication) getActivity().getApplication();
-                application.setCurrent(current);
+               final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+               store.saveCurrent(current);
             }
         }     
         
@@ -106,18 +106,25 @@ public class CurrentFragment extends Fragment {
                                                // 1. Check conditions. They must be the same as the ones that triggered the AsyncTask.
                                                final DatabaseQueries query = new DatabaseQueries(CurrentFragment.this.getActivity().getApplicationContext());
                                    final WeatherLocation weatherLocation = query.queryDataBase();
-                                   final WeatherInformationApplication application =
-                                               (WeatherInformationApplication) CurrentFragment.this.getActivity().getApplication();
-                                   final Current current = application.getCurrent();
+                                   final PermanentStorage store = new PermanentStorage(CurrentFragment.this.getActivity().getApplicationContext());
+                                   final Current current = store.getCurrent();
 
                                    if (current == null || !CurrentFragment.this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
                                        // 2. Update UI.
                                        CurrentFragment.this.updateUI(currentRemote);
 
                                        // 3. Update Data.
-                                           application.setCurrent(currentRemote);
+                                                       store.saveCurrent(currentRemote);
                                            weatherLocation.setLastCurrentUIUpdate(new Date());
                                            query.updateDataBase(weatherLocation);
+
+                                           // 4. Update Widget's UI.
+                                           // TODO: Unable to start service intent not found U=0  WHYYYYYYYY? ANDROID SUCKSSSSSSS
+                                           final Intent intentWidget = new Intent();
+                                           intentWidget.setClassName("de.example.exampletdd", WidgetIntentService.class.getCanonicalName());
+                                           intent.putExtra("appWidgetId", 0);
+                                           intentWidget.putExtra("updateByApp", true);
+                                           CurrentFragment.this.getActivity().getApplicationContext().startService(intent);
                                    }
 
                                        } else {
@@ -152,10 +159,9 @@ public class CurrentFragment extends Fragment {
                errorMessage.setVisibility(View.VISIBLE);
             return;
         }
-        
-        final WeatherInformationApplication application =
-                       (WeatherInformationApplication) getActivity().getApplication();
-        final Current current = application.getCurrent();
+
+        final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+        final Current current = store.getCurrent();
 
         if (current != null && this.isDataFresh(weatherLocation.getLastCurrentUIUpdate())) {
             this.updateUI(current);
@@ -180,9 +186,8 @@ public class CurrentFragment extends Fragment {
     public void onSaveInstanceState(final Bundle savedInstanceState) {
 
         // Save UI state
-       final WeatherInformationApplication application =
-                       (WeatherInformationApplication) getActivity().getApplication();
-        final Current current = application.getCurrent();
+       final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+        final Current current = store.getCurrent();
 
         // TODO: Could it be better to save current data even if it is null value?
         //       So, perhaps do not check for null value.
index 907791d..c24bfdd 100644 (file)
@@ -36,7 +36,6 @@ import android.widget.ListView;
 import com.fasterxml.jackson.core.JsonParseException;
 
 import de.example.exampletdd.R;
-import de.example.exampletdd.WeatherInformationApplication;
 import de.example.exampletdd.fragment.specific.SpecificFragment;
 import de.example.exampletdd.httpclient.CustomHTTPClient;
 import de.example.exampletdd.model.DatabaseQueries;
@@ -44,6 +43,7 @@ import de.example.exampletdd.model.WeatherLocation;
 import de.example.exampletdd.model.forecastweather.Forecast;
 import de.example.exampletdd.parser.JPOSWeatherParser;
 import de.example.exampletdd.service.IconsList;
+import de.example.exampletdd.service.PermanentStorage;
 import de.example.exampletdd.service.ServiceParser;
 
 public class OverviewFragment extends ListFragment {
@@ -69,9 +69,8 @@ public class OverviewFragment extends ListFragment {
             // TODO: Could it be better to store in global forecast data even if it is null value?
             //       So, perhaps do not check for null value and always store in global variable.
             if (forecast != null) {
-                final WeatherInformationApplication application =
-                               (WeatherInformationApplication) getActivity().getApplication();
-                application.setForecast(forecast);
+               final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+               store.saveForecast(forecast);
             }
         }
 
@@ -99,16 +98,15 @@ public class OverviewFragment extends ListFragment {
                                                // 1. Check conditions. They must be the same as the ones that triggered the AsyncTask.
                                                final DatabaseQueries query = new DatabaseQueries(OverviewFragment.this.getActivity().getApplicationContext());
                                    final WeatherLocation weatherLocation = query.queryDataBase();
-                                               final WeatherInformationApplication application =
-                                               (WeatherInformationApplication) OverviewFragment.this.getActivity().getApplication();
-                                               final Forecast forecast = application.getForecast();
+                                   final PermanentStorage store = new PermanentStorage(OverviewFragment.this.getActivity().getApplicationContext());
+                                   final Forecast forecast = store.getForecast();
 
                                        if (forecast == null || !OverviewFragment.this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
                                                // 2. Update UI.
                                                OverviewFragment.this.updateUI(forecastRemote);
 
                                                // 3. Update Data.
-                                                       application.setForecast(forecastRemote);
+                                               store.saveForecast(forecastRemote);
                                            weatherLocation.setLastForecastUIUpdate(new Date());
                                            query.updateDataBase(weatherLocation);
 
@@ -141,10 +139,10 @@ public class OverviewFragment extends ListFragment {
             return;
         }
 
-        final WeatherInformationApplication application =
-                       (WeatherInformationApplication) getActivity().getApplication();
-        final Forecast forecast = application.getForecast();
+        final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+        final Forecast forecast = store.getForecast();
 
+        // TODO: store forecast data in permanent storage and check here if there is data in permanent storage
         if (forecast != null && this.isDataFresh(weatherLocation.getLastForecastUIUpdate())) {
             this.updateUI(forecast);
         } else {
@@ -165,9 +163,8 @@ public class OverviewFragment extends ListFragment {
     public void onSaveInstanceState(final Bundle savedInstanceState) {
 
         // Save UI state
-        final WeatherInformationApplication application =
-                       (WeatherInformationApplication) getActivity().getApplication();
-        final Forecast forecast = application.getForecast();
+       final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+        final Forecast forecast = store.getForecast();
 
         // TODO: Could it be better to save forecast data even if it is null value?
         //       So, perhaps do not check for null value.
index f8a4838..2b665e9 100644 (file)
@@ -11,7 +11,7 @@ import android.os.SystemClock;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import de.example.exampletdd.R;
-import de.example.exampletdd.WeatherInformationBatch;
+import de.example.exampletdd.NotificationIntentService;
 
 public class WeatherInformationPreferencesFragment extends PreferenceFragment 
                                                                                                        implements OnSharedPreferenceChangeListener {
@@ -163,7 +163,7 @@ public class WeatherInformationPreferencesFragment extends PreferenceFragment
         // TODO: better use some string instead of .class? In case I change the service class
         // this could be a problem (I guess)
         final Intent serviceIntent =
-                       new Intent(this.getActivity().getApplicationContext(), WeatherInformationBatch.class);
+                       new Intent(this.getActivity().getApplicationContext(), NotificationIntentService.class);
         final PendingIntent alarmIntent =
                        PendingIntent.getService(
                                        this.getActivity().getApplicationContext(),
index dbe55e9..0a4cc93 100644 (file)
@@ -19,9 +19,9 @@ import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 import de.example.exampletdd.R;
-import de.example.exampletdd.WeatherInformationApplication;
 import de.example.exampletdd.model.forecastweather.Forecast;
 import de.example.exampletdd.service.IconsList;
+import de.example.exampletdd.service.PermanentStorage;
 
 public class SpecificFragment extends Fragment {
     private int mChosenDay;
@@ -61,9 +61,8 @@ public class SpecificFragment extends Fragment {
             // TODO: Could it be better to store in global data forecast even if it is null value?
             //       So, perhaps do not check for null value and always store in global variable.
             if (forecast != null) {
-                final WeatherInformationApplication application =
-                               (WeatherInformationApplication) getActivity().getApplication();
-                application.setForecast(forecast);
+               final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+               store.saveForecast(forecast);
             }
 
             this.mChosenDay = savedInstanceState.getInt("mChosenDay");
@@ -76,9 +75,8 @@ public class SpecificFragment extends Fragment {
     public void onSaveInstanceState(final Bundle savedInstanceState) {
 
         // Save UI state
-       final WeatherInformationApplication application =
-                       (WeatherInformationApplication) getActivity().getApplication();
-        final Forecast forecast = application.getForecast();
+       final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+        final Forecast forecast = store.getForecast();
 
         // TODO: Could it be better to save forecast data even if it is null value?
         //       So, perhaps do not check for null value.
@@ -97,9 +95,8 @@ public class SpecificFragment extends Fragment {
      * @param chosenDay
      */
     public void updateUIByChosenDay(final int chosenDay) {
-        final WeatherInformationApplication application =
-                       (WeatherInformationApplication) getActivity().getApplication();
-        final Forecast forecast = application.getForecast();
+       final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+        final Forecast forecast = store.getForecast();
 
         if (forecast != null) {
             this.updateUI(forecast, chosenDay);
@@ -264,9 +261,8 @@ public class SpecificFragment extends Fragment {
     public void onResume() {
         super.onResume();
 
-        final WeatherInformationApplication application =
-                       (WeatherInformationApplication) getActivity().getApplication();
-        final Forecast forecast = application.getForecast();
+        final PermanentStorage store = new PermanentStorage(this.getActivity().getApplicationContext());
+        final Forecast forecast = store.getForecast();
 
         if (forecast != null) {
             this.updateUI(forecast, this.mChosenDay);
diff --git a/Android/WeatherInformation/src/de/example/exampletdd/service/PermanentStorage.java b/Android/WeatherInformation/src/de/example/exampletdd/service/PermanentStorage.java
new file mode 100644 (file)
index 0000000..49eac40
--- /dev/null
@@ -0,0 +1,140 @@
+package de.example.exampletdd.service;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.StreamCorruptedException;
+
+import android.content.Context;
+import android.util.Log;
+import de.example.exampletdd.model.currentweather.Current;
+import de.example.exampletdd.model.forecastweather.Forecast;
+
+
+/**
+ * TODO: show some error message when there is no enough space for saving files. :/
+ *
+ */
+public class PermanentStorage {
+       private static final String TAG = "PermanentStorage";
+    private static final String CURRENT_DATA_FILE = "current.file";
+    private static final String FORECAST_DATA_FILE = "forecast.file";
+    private final Context context;
+
+    public PermanentStorage(final Context context) {
+        this.context = context;
+    }
+
+    public void saveCurrent(final Current current) {
+       
+        try {
+                       this.saveObject(CURRENT_DATA_FILE, current);
+               } catch (FileNotFoundException e) {
+                       Log.e(TAG, "saveCurrent exception: ", e);
+               } catch (IOException e) {
+                       Log.e(TAG, "saveCurrent exception: ", e);
+               }
+    }
+
+    public Current getCurrent() {
+       
+       try {
+                       return (Current) this.getObject(CURRENT_DATA_FILE);
+               } catch (final StreamCorruptedException e) {
+                       Log.e(TAG, "getCurrent exception: ", e);
+               } catch (final FileNotFoundException e) {
+                       Log.e(TAG, "getCurrent exception: ", e);
+               } catch (final IOException e) {
+                       Log.e(TAG, "getCurrent exception: ", e);
+               } catch (final ClassNotFoundException e) {
+                       Log.e(TAG, "getCurrent exception: ", e);
+               }
+       
+       return null;
+    }
+
+    public void saveForecast(final Forecast forecast) {
+
+       try {
+                       this.saveObject(FORECAST_DATA_FILE, forecast);
+               } catch (FileNotFoundException e) {
+                       Log.e(TAG, "saveForecast exception: ", e);
+               } catch (IOException e) {
+                       Log.e(TAG, "saveForecast exception: ", e);
+               }
+    }
+
+    public Forecast getForecast() {
+        
+       try {
+                       return (Forecast) this.getObject(FORECAST_DATA_FILE);
+               } catch (final StreamCorruptedException e) {
+                       Log.e(TAG, "getForecast exception: ", e);
+               } catch (final FileNotFoundException e) {
+                       Log.e(TAG, "getForecast exception: ", e);
+               } catch (final IOException e) {
+                       Log.e(TAG, "getForecast exception: ", e);
+               } catch (final ClassNotFoundException e) {
+                       Log.e(TAG, "getForecast exception: ", e);
+               }
+       
+       return null;
+    }
+
+    private void saveObject(final String fileName, final Object objectToStore)
+               throws FileNotFoundException, IOException {
+       final String temporaryFileName = fileName.concat(".tmp");
+       
+        final FileOutputStream tmpPersistFile = this.context.openFileOutput(
+                       temporaryFileName, Context.MODE_PRIVATE);
+        try {
+               final ObjectOutputStream oos = new ObjectOutputStream(tmpPersistFile);
+               try {
+                       oos.writeObject(objectToStore);
+                       
+                       // Don't fear the fsync!
+                       // http://thunk.org/tytso/blog/2009/03/15/dont-fear-the-fsync/
+                       tmpPersistFile.flush();
+                       tmpPersistFile.getFD().sync();
+               } finally {
+                       oos.close();
+               }
+        } finally {
+               tmpPersistFile.close();
+        }
+
+        this.renameFile(temporaryFileName, fileName);
+    }
+    private Object getObject(final String fileName) throws StreamCorruptedException, FileNotFoundException,
+                                                                                                          IOException, ClassNotFoundException {
+       final InputStream persistFile = this.context.openFileInput(fileName);
+       try {
+               final ObjectInputStream ois = new ObjectInputStream(persistFile);
+               try {
+                       return ois.readObject();
+               } finally {
+                       ois.close();
+               }
+       } finally {
+               persistFile.close();
+       }
+    } 
+    
+    private void renameFile(final String fromFileName, final String toFileName) throws IOException {
+        final File filesDir = this.context.getFilesDir();
+        final File fromFile = new File(filesDir, fromFileName);
+        final File toFile = new File(filesDir, toFileName);
+        if (!fromFile.renameTo(toFile)) {
+               if (!fromFile.delete()) {
+                       throw new IOException("PermanentStorage, delete file error");
+               }       
+               throw new IOException("PermanentStorage, rename file error");
+        }
+    }
+}
+
index 553ba80..4c59fd5 100644 (file)
@@ -1,52 +1,18 @@
 package de.example.exampletdd.widget;
 
 
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.Calendar;
-import java.util.Locale;
-
-import org.apache.http.client.ClientProtocolException;
-
-import android.app.IntentService;
-import android.app.PendingIntent;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.http.AndroidHttpClient;
 import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.support.v4.app.TaskStackBuilder;
-import android.util.Log;
-import android.widget.RemoteViews;
-
-import com.fasterxml.jackson.core.JsonParseException;
-
-import de.example.exampletdd.R;
-import de.example.exampletdd.WeatherTabsActivity;
-import de.example.exampletdd.httpclient.CustomHTTPClient;
-import de.example.exampletdd.model.DatabaseQueries;
-import de.example.exampletdd.model.WeatherLocation;
-import de.example.exampletdd.model.currentweather.Current;
-import de.example.exampletdd.parser.JPOSWeatherParser;
-import de.example.exampletdd.service.IconsList;
-import de.example.exampletdd.service.ServiceParser;
+import de.example.exampletdd.WidgetIntentService;
 
 public class WeatherInformationWidgetProvider extends AppWidgetProvider {
-    private static final String TAG = "WeatherInformationWidgetProvider";
 
     @Override
     public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds) {
-        Log.d(TAG, "onUpdate");
         // For each widget that needs an update, get the text that we should display:
         //   - Create a RemoteViews object for it
         //   - Set the text in the RemoteViews object
@@ -55,16 +21,15 @@ public class WeatherInformationWidgetProvider extends AppWidgetProvider {
         for (int i=0; i<N; i++) {
             int appWidgetId = appWidgetIds[i];
             // To prevent any ANR timeouts, we perform the update in a service
-               final Intent intent = new Intent(context, UpdateService.class);
+               final Intent intent = new Intent(context, WidgetIntentService.class);
                intent.putExtra("appWidgetId", appWidgetId);
+               intent.putExtra("updateByApp", false);
             context.startService(intent);
-            //updateAppWidget(context, appWidgetManager, appWidgetId);
         }
     }
     
     @Override
     public void onDeleted(Context context, int[] appWidgetIds) {
-        Log.d(TAG, "onDeleted");
         // When the user deletes the widget, delete the preference associated with it.
         final int N = appWidgetIds.length;
         for (int i=0; i<N; i++) {
@@ -73,7 +38,6 @@ public class WeatherInformationWidgetProvider extends AppWidgetProvider {
     }
     
     static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager, final int appWidgetId) {
-        Log.i(TAG, "updateAppWidget appWidgetId=" + appWidgetId);
 
         int widgetId;
         Bundle myOptions = appWidgetManager.getAppWidgetOptions(appWidgetId);
@@ -91,174 +55,10 @@ public class WeatherInformationWidgetProvider extends AppWidgetProvider {
         // Construct the RemoteViews object.  It takes the package name (in our case, it's our
         // package, but it needs this because on the other side it's the widget host inflating
         // the layout from our package).
-        final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
-
-        // Tell the widget manager
-        appWidgetManager.updateAppWidget(appWidgetId, views);
-    }
-    
-    public static class UpdateService extends IntentService {
-        private static final String TAG = "UpdateService";
-
-
-        public UpdateService() {
-            super("UpdateWidget-Thread");
-        }
-
-        @Override
-        protected void onHandleIntent(final Intent intent) {
-               int appWidgetId = intent.getIntExtra("appWidgetId", -666);
-               
-               if (appWidgetId == -666) {
-                       // Nothing to do. Something went wrong.
-                       Log.e(TAG, "appWidgetId error value");
-                       return;
-               }
-               
-            final DatabaseQueries query = new DatabaseQueries(this.getApplicationContext());
-            final WeatherLocation weatherLocation = query.queryDataBase();
-            
-            if (weatherLocation != null) {
-                final ServiceParser weatherService = new ServiceParser(new JPOSWeatherParser());
-                final CustomHTTPClient HTTPClient = new CustomHTTPClient(
-                        AndroidHttpClient.newInstance("Android 4.3 WeatherInformation Agent"));
-
-                Current current = null;
-                try {
-                       current = this.doInBackgroundThrowable(weatherLocation, HTTPClient, weatherService);
-                    
-                } catch (final JsonParseException e) {
-                    Log.e(TAG, "doInBackground exception: ", e);
-                } catch (final ClientProtocolException e) {
-                    Log.e(TAG, "doInBackground exception: ", e);
-                } catch (final MalformedURLException e) {
-                    Log.e(TAG, "doInBackground exception: ", e);
-                } catch (final URISyntaxException e) {
-                    Log.e(TAG, "doInBackground exception: ", e);
-                } catch (final IOException e) {
-                    // logger infrastructure swallows UnknownHostException :/
-                    Log.e(TAG, "doInBackground exception: " + e.getMessage(), e);
-                } finally {
-                    HTTPClient.close();
-                }
-                
-                if (current != null) {
-                       this.updateWidget(current, weatherLocation, appWidgetId);
-                } else {
-                       // TODO: show layout error in Widget
-                }
-                
-            } else {
-               // TODO: show layout error in Widget
-            }
-        }
-
-        private Current doInBackgroundThrowable(final WeatherLocation weatherLocation,
-                final CustomHTTPClient HTTPClient, final ServiceParser weatherService)
-                        throws ClientProtocolException, MalformedURLException, URISyntaxException,
-                        JsonParseException, IOException {
-
-            final String APIVersion = this.getResources().getString(R.string.api_version);
-
-            final String urlAPI = this.getResources().getString(R.string.uri_api_weather_today);
-            final String url = weatherService.createURIAPICurrent(urlAPI, APIVersion,
-                    weatherLocation.getLatitude(), weatherLocation.getLongitude());
-            final String jsonData = HTTPClient.retrieveDataAsString(new URL(url));
-            final Current current = weatherService.retrieveCurrentFromJPOS(jsonData);
-            // TODO: what is this for? I guess I could skip it :/
-            final Calendar now = Calendar.getInstance();
-            current.setDate(now.getTime());
-            
-            return current;
-        }
+        //final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
         
-        private void updateWidget(final Current current, final WeatherLocation weatherLocation, final int appWidgetId) {
-            final SharedPreferences sharedPreferences = PreferenceManager
-                    .getDefaultSharedPreferences(this.getApplicationContext());
-
-            // TODO: repeating the same code in Overview, Specific and Current!!!
-            // 1. Update units of measurement.
-            boolean mIsFahrenheit = false;
-            final String keyPreference = this.getResources().getString(
-                    R.string.weather_preferences_units_key);
-            final String unitsPreferenceValue = sharedPreferences.getString(keyPreference, "");
-            final String celsius = this.getResources().getString(
-                    R.string.weather_preferences_units_celsius);
-            if (unitsPreferenceValue.equals(celsius)) {
-                mIsFahrenheit = false;
-            } else {
-                mIsFahrenheit = true;
-            }
-            final double tempUnits = mIsFahrenheit ? 0 : 273.15;
-            final String symbol = mIsFahrenheit ? "ºF" : "ºC";
-
-
-            // 2. Formatters
-            final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
-            tempFormatter.applyPattern("#####.#####");
-
-
-            // 3. Prepare data for RemoteViews.
-            String tempMax = "";
-            if (current.getMain().getTemp_max() != null) {
-                double conversion = (Double) current.getMain().getTemp_max();
-                conversion = conversion - tempUnits;
-                tempMax = tempFormatter.format(conversion) + symbol;
-            }
-            String tempMin = "";
-            if (current.getMain().getTemp_min() != null) {
-                double conversion = (Double) current.getMain().getTemp_min();
-                conversion = conversion - tempUnits;
-                tempMin = tempFormatter.format(conversion) + symbol;
-            }
-            Bitmap picture;
-            if ((current.getWeather().size() > 0)
-                    && (current.getWeather().get(0).getIcon() != null)
-                    && (IconsList.getIcon(current.getWeather().get(0).getIcon()) != null)) {
-                final String icon = current.getWeather().get(0).getIcon();
-                picture = BitmapFactory.decodeResource(this.getResources(), IconsList.getIcon(icon)
-                        .getResourceDrawable());
-            } else {
-                picture = BitmapFactory.decodeResource(this.getResources(),
-                        R.drawable.weather_severe_alert);
-            }
-            final String city = weatherLocation.getCity();
-            final String country = weatherLocation.getCountry();
-            
-            // 4. Insert data in RemoteViews.
-            final RemoteViews remoteView = new RemoteViews(this.getApplicationContext().getPackageName(), R.layout.appwidget);
-            remoteView.setImageViewBitmap(R.id.weather_appwidget_image, picture);
-            remoteView.setTextViewText(R.id.weather_appwidget_temperature_max, tempMax);
-            remoteView.setTextViewText(R.id.weather_appwidget_temperature_min, tempMin);
-            remoteView.setTextViewText(R.id.weather_appwidget_city, city);
-            remoteView.setTextViewText(R.id.weather_appwidget_country, country);
-
-            // 5. Activity launcher.
-            final Intent resultIntent =  new Intent(this.getApplicationContext(), WeatherTabsActivity.class);
-            // The PendingIntent to launch our activity if the user selects this notification
-//            final PendingIntent contentIntent = PendingIntent.getActivity(
-//                     this.getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            // The stack builder object will contain an artificial back stack for the started Activity.
-            // This ensures that navigating backward from the Activity leads out of
-            // your application to the Home screen.
-            final TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.getApplicationContext());
-            // Adds the back stack for the Intent (but not the Intent itself)
-            stackBuilder.addParentStack(WeatherTabsActivity.class);
-            // Adds the Intent that starts the Activity to the top of the stack
-            stackBuilder.addNextIntent(resultIntent);
-            final PendingIntent resultPendingIntent =
-                       stackBuilder.getPendingIntent(
-                        0,
-                        PendingIntent.FLAG_UPDATE_CURRENT
-                    );
-            remoteView.setOnClickPendingIntent(R.id.weather_appwidget, resultPendingIntent);
-            
-            
-            // Push update for this widget to the home screen
-            final AppWidgetManager manager = AppWidgetManager.getInstance(this);
-            manager.updateAppWidget(appWidgetId, remoteView);
-            
-            Log.i("WordWidget.UpdateService", "widget updated");
-        }
+        final Intent intent = new Intent(context, WidgetIntentService.class);
+       intent.putExtra("appWidgetId", appWidgetId);
+        context.startService(intent);
     }
 }