Enable computation of the real sunrise/sunset times in Grass.

Change-Id: I6fce63645f204774b9bfde0cee625c71fa78e1a7
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index fdd3052..685ec59 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -21,6 +21,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.wallpaper">
 
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
     <application
         android:label="@string/wallpapers"
         android:icon="@drawable/ic_launcher_wallpaper">
diff --git a/res/raw/grass.rs b/res/raw/grass.rs
index 09a5ecb..56405b8 100644
--- a/res/raw/grass.rs
+++ b/res/raw/grass.rs
@@ -41,11 +41,6 @@
 
 #define MAX_BEND 0.09f
 
-#define MIDNIGHT 0.0f
-#define MORNING 0.3f
-#define AFTERNOON 0.75f
-#define DUSK 0.84f
-
 #define SECONDS_IN_DAY 86400.0f
 
 #define PI 3.1415926f
@@ -57,7 +52,7 @@
     if (REAL_TIME) {
         return (hour() * 3600.0f + minute() * 60.0f + second()) / SECONDS_IN_DAY;
     }
-    float t = uptimeMillis() / 20000.0f;
+    float t = uptimeMillis() / 40000.0f;
     return t - (int) t;
 }
 
@@ -93,7 +88,9 @@
     drawRect(0.0f, 0.0f, width, height, 0.0f);
 }
 
-int drawBlade(float *bladeStruct, float *bladeBuffer, int *bladeColor, float now, float xOffset) {
+int drawBlade(float *bladeStruct, float *bladeBuffer, int *bladeColor,
+        float brightness, float xOffset) {
+
     float offset = bladeStruct[BLADE_STRUCT_OFFSET];
     float scale = bladeStruct[BLADE_STRUCT_SCALE];
     float angle = bladeStruct[BLADE_STRUCT_ANGLE];
@@ -112,20 +109,7 @@
     float s = bladeStruct[BLADE_STRUCT_S];
     float b = bladeStruct[BLADE_STRUCT_B];
 
-    float newB = 1.0f;
-    if (now >= MIDNIGHT && now < MORNING) {
-        newB = now / MORNING;
-    }
-
-    if (now >= AFTERNOON && now < DUSK) {
-        newB = 1.0f - normf(AFTERNOON, DUSK, now);
-    }
-
-    if (now >= DUSK) {
-        newB = 0.0f;
-    }
-
-    int color = hsbToAbgr(h, s, lerpf(0, b, newB), 1.0f);
+    int color = hsbToAbgr(h, s, lerpf(0, b, brightness), 1.0f);
 
     float newAngle = turbulencef2(turbulenceX, uptimeMillis() * 0.00004f, 4.0f) - 0.5f;
     newAngle *= 0.5f;
@@ -203,7 +187,7 @@
     return triangles * 15;
 }
 
-void drawBlades(float now, float xOffset) {
+void drawBlades(float brightness, float xOffset) {
     // For anti-aliasing
     bindTexture(NAMED_PFBackground, 0, NAMED_TAa);
 
@@ -216,7 +200,7 @@
     int *bladeColor = loadArrayI32(RSID_BLADES_BUFFER, 0);
 
     for ( ; i < bladesCount; i += 1) {
-        int offset = drawBlade(bladeStruct, bladeBuffer, bladeColor, now, xOffset);
+        int offset = drawBlade(bladeStruct, bladeBuffer, bladeColor, brightness, xOffset);
         bladeBuffer += offset;
         bladeColor += offset;
         bladeStruct += BLADE_STRUCT_FIELDS_COUNT;
@@ -235,25 +219,49 @@
     float now = time();
     alpha(1.0f);
 
-    if (now >= MIDNIGHT && now < MORNING) {
+    float newB = 1.0f;
+    float dawn = State->dawn;
+    float morning = State->morning;
+    float afternoon = State->afternoon;
+    float dusk = State->dusk;
+
+    if (now >= 0.0f && now < dawn) {                    // Draw night
         drawNight(width, height);
-        alpha(normf(MIDNIGHT, MORNING, now));
-        drawSunrise(width, height);
-    } else if (now >= MORNING && now < AFTERNOON) {
-        drawSunrise(width, height);
-        alpha(normf(MORNING, AFTERNOON, now));
+        newB = 0.0f;
+    } else if (now >= dawn && now <= morning) {         // Draw sunrise
+        float half = dawn + (morning - dawn) * 0.5f;
+        if (now <= half) {                              // Draw night->sunrise
+            drawNight(width, height);
+            newB = normf(dawn, half, now);
+            alpha(newB);
+            drawSunrise(width, height);
+        } else {                                        // Draw sunrise->day
+            drawSunrise(width, height);
+            alpha(normf(half, morning, now));
+            drawNoon(width, height);    
+        }
+    } else if (now > morning && now < afternoon) {      // Draw day
         drawNoon(width, height);
-    } else if (now >= AFTERNOON && now < DUSK) {
-        drawNoon(width, height);
-        alpha(normf(AFTERNOON, DUSK, now));
-        drawSunset(width, height);
-    } else if (now >= DUSK) {
+    } else if (now >= afternoon && now <= dusk) {       // Draw sunset
+        float half = afternoon + (dusk - afternoon) * 0.5f;
+        if (now <= half) {                              // Draw day->sunset
+            drawNoon(width, height);
+            newB = normf(afternoon, half, now);
+            alpha(newB);
+            newB = 1.0f - newB;
+            drawSunset(width, height);
+        } else {                                        // Draw sunset->night
+            drawSunset(width, height);
+            alpha(normf(half, dusk, now));
+            drawNight(width, height);
+            newB = 0.0f;
+        }
+    } else if (now > dusk) {                            // Draw night
         drawNight(width, height);
-        alpha(1.0f - normf(DUSK, 1.0f, now));
-        drawSunset(width, height);
+        newB = 0.0f;
     }
 
-    drawBlades(now, x);
+    drawBlades(newB, x);
 
     return 1;
 }
diff --git a/src/com/android/wallpaper/grass/GrassRS.java b/src/com/android/wallpaper/grass/GrassRS.java
index 8e01b6b..e824d7e 100644
--- a/src/com/android/wallpaper/grass/GrassRS.java
+++ b/src/com/android/wallpaper/grass/GrassRS.java
@@ -34,18 +34,29 @@
 import android.renderscript.SimpleMesh;
 import android.renderscript.Primitive;
 import static android.renderscript.Sampler.Value.*;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.location.LocationManager;
+import android.location.LocationListener;
+import android.location.Location;
+import android.os.Bundle;
+import android.text.format.Time;
 import com.android.wallpaper.R;
 import com.android.wallpaper.RenderScriptScene;
 
 import java.util.TimeZone;
+import java.util.Calendar;
 
 class GrassRS extends RenderScriptScene {
+    private static final int LOCATION_UPDATE_MIN_TIME = 60 * 60 * 1000; // 1 hour
+    private static final int LOCATION_UPDATE_MIN_DISTANCE = 150 * 1000; // 150 km
+
     private static final float TESSELATION = 0.5f;
-
-    private static final int RSID_STATE = 0;
-
     private static final int TEXTURES_COUNT = 5;
 
+    private static final int RSID_STATE = 0;
     private static final int RSID_BLADES = 1;
     private static final int BLADES_COUNT = 200;
     private static final int BLADE_STRUCT_FIELDS_COUNT = 13;
@@ -88,12 +99,60 @@
     private SimpleMesh mBladesMesh;
 
     private int mTriangles;
-    private final float[] mFloatData5 = new float[5];
-    private WorldState mWorldState;
     private float[] mBladesData;
+    private final float[] mFloatData5 = new float[5];
 
-    GrassRS(int width, int height) {
+    private WorldState mWorldState;
+
+    private final Context mContext;
+    private final LocationManager mLocationManager;
+
+    private LocationUpdater mLocationUpdater;
+    private GrassRS.TimezoneTracker mTimezoneTracker;
+
+    GrassRS(Context context, int width, int height) {
         super(width, height);
+
+        mContext = context;
+        mLocationManager = (LocationManager)
+                context.getSystemService(Context.LOCATION_SERVICE);
+    }
+
+    @Override
+    public void start() {
+        super.start();
+
+        if (mTimezoneTracker == null) {
+            mTimezoneTracker = new TimezoneTracker();
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_TIME_CHANGED);
+            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+    
+            mContext.registerReceiver(mTimezoneTracker, filter);
+        }
+
+        if (mLocationUpdater == null) {
+            mLocationUpdater = new LocationUpdater();
+            mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+                    LOCATION_UPDATE_MIN_TIME, LOCATION_UPDATE_MIN_DISTANCE, mLocationUpdater);
+        }
+
+        updateLocation();
+    }
+
+    @Override
+    public void stop() {
+        super.stop();
+
+        if (mTimezoneTracker != null) {
+            mContext.unregisterReceiver(mTimezoneTracker);
+            mTimezoneTracker = null;
+        }
+        
+        if (mLocationUpdater != null) {
+            mLocationManager.removeUpdates(mLocationUpdater);
+            mLocationUpdater = null;
+        }
     }
 
     @Override
@@ -154,6 +213,10 @@
         public int width;
         public int height;
         public float xOffset;
+        public float dawn;
+        public float morning;
+        public float afternoon;
+        public float dusk;
     }
 
     private void createState() {
@@ -342,4 +405,53 @@
         mPvBackground.bindAllocation(mPvOrthoAlloc);
         mPvBackground.setName("PVBackground");
     }
+
+    private void updateLocation() {
+        updateLocation(mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
+    }
+
+    private void updateLocation(Location location) {
+        if (location != null) {
+            final String timeZone = Time.getCurrentTimezone();
+            final SunCalculator calculator = new SunCalculator(location, timeZone);
+            final Calendar now = Calendar.getInstance();
+
+            final double sunrise = calculator.computeSunriseTime(SunCalculator.ZENITH_CIVIL, now);
+            mWorldState.dawn = SunCalculator.timeToDayFraction(sunrise);
+
+            final double sunset = calculator.computeSunsetTime(SunCalculator.ZENITH_CIVIL, now);
+            mWorldState.dusk = SunCalculator.timeToDayFraction(sunset);
+        } else {
+            mWorldState.dawn = 0.3f;
+            mWorldState.dusk = 0.75f;
+        }
+
+        mWorldState.morning = mWorldState.dawn + 1.0f / 12.0f; // 2 hours for sunrise
+        mWorldState.afternoon = mWorldState.dusk - 1.0f / 12.0f; // 2 hours for sunset
+
+        // Send the new data to RenderScript
+        mState.data(mWorldState);
+    }
+
+    private class LocationUpdater implements LocationListener {
+        public void onLocationChanged(Location location) {
+            updateLocation(location);
+        }
+
+        public void onStatusChanged(String provider, int status, Bundle extras) {
+        }
+
+        public void onProviderEnabled(String provider) {
+        }
+
+        public void onProviderDisabled(String provider) {
+        }
+    }
+
+    private class TimezoneTracker extends BroadcastReceiver {
+        public void onReceive(Context context, Intent intent) {
+            getScript().setTimeZone(Time.getCurrentTimezone());
+            updateLocation();
+        }
+    }
 }
diff --git a/src/com/android/wallpaper/grass/GrassView.java b/src/com/android/wallpaper/grass/GrassView.java
index 3c1b167..80ecf8f 100644
--- a/src/com/android/wallpaper/grass/GrassView.java
+++ b/src/com/android/wallpaper/grass/GrassView.java
@@ -34,7 +34,7 @@
         super.surfaceChanged(holder, format, w, h);
 
         RenderScript RS = createRenderScript(false);
-        GrassRS render = new GrassRS(w, h);
+        GrassRS render = new GrassRS(getContext(), w, h);
         render.init(RS, getResources(), false);
         render.setOffset(0.5f, 0.0f, 0, 0);        
         render.start();
diff --git a/src/com/android/wallpaper/grass/GrassWallpaper.java b/src/com/android/wallpaper/grass/GrassWallpaper.java
index eabc170..01329a8 100644
--- a/src/com/android/wallpaper/grass/GrassWallpaper.java
+++ b/src/com/android/wallpaper/grass/GrassWallpaper.java
@@ -21,7 +21,7 @@
 
 public class GrassWallpaper extends RenderScriptWallpaper {
     protected RenderScriptScene createScene(int width, int height) {
-        return new GrassRS(width, height);
+        return new GrassRS(this, width, height);
     }
 }