Refactor RenderScript wallpapers to share more code between the implementations.

Change-Id: I5995f68438531396cfae80c27e5d7781effaaa31
diff --git a/res/raw/grass.rs b/res/raw/grass.rs
index b3846c2..696ef75 100644
--- a/res/raw/grass.rs
+++ b/res/raw/grass.rs
@@ -53,11 +53,12 @@
 
 #define REAL_TIME 0
 
-float time(int frameCount) {
+float time() {
     if (REAL_TIME) {
         return (hour() * 3600.0f + minute() * 60.0f + second()) / SECONDS_IN_DAY;
     }
-    return (frameCount % 180) / 180.0f;
+    float t = uptimeMillis() / 20000.0f;
+    return t - (int) t;
 }
 
 void alpha(float a) {
@@ -92,14 +93,14 @@
     drawRect(0.0f, 0.0f, width, height, 0.0f);
 }
 
-int drawBlade(float *bladeStruct, float *bladeBuffer, int *bladeColor, float now, int frameCount) {
+int drawBlade(float *bladeStruct, float *bladeBuffer, int *bladeColor, float now, float xOffset) {
     float offset = bladeStruct[BLADE_STRUCT_OFFSET];
     float scale = bladeStruct[BLADE_STRUCT_SCALE];
     float angle = bladeStruct[BLADE_STRUCT_ANGLE];
     float hardness = bladeStruct[BLADE_STRUCT_HARDNESS];
     float turbulenceX = bladeStruct[BLADE_STRUCT_TURBULENCEX];
 
-    float xpos = bladeStruct[BLADE_STRUCT_XPOS];
+    float xpos = bladeStruct[BLADE_STRUCT_XPOS] + xOffset;
     float ypos = bladeStruct[BLADE_STRUCT_YPOS];
 
     float lengthX = bladeStruct[BLADE_STRUCT_LENGTHX];
@@ -126,7 +127,7 @@
 
     int color = hsbToAbgr(h, s, lerpf(0, b, newB), 1.0f);
 
-    float newAngle = turbulencef2(turbulenceX, frameCount * 0.006f, 4.0f) - 0.5f;
+    float newAngle = turbulencef2(turbulenceX, uptimeMillis() * 0.00004f, 4.0f) - 0.5f;
     newAngle *= 0.5f;
     angle = clampf(angle + (newAngle + offset - angle) * 0.15f, -MAX_BEND, MAX_BEND);
 
@@ -202,7 +203,7 @@
     return triangles * 15;
 }
 
-void drawBlades(float now, int frameCount) {
+void drawBlades(float now, float xOffset) {
     // For anti-aliasing
     bindTexture(NAMED_PFBackground, 0, NAMED_TAa);
 
@@ -215,7 +216,7 @@
     int *bladeColor = loadArrayI32(RSID_BLADES_BUFFER, 0);
 
     for ( ; i < bladesCount; i += 1) {
-        int offset = drawBlade(bladeStruct, bladeBuffer, bladeColor, now, frameCount);
+        int offset = drawBlade(bladeStruct, bladeBuffer, bladeColor, now, xOffset);
         bladeBuffer += offset;
         bladeColor += offset;
         bladeStruct += BLADE_STRUCT_FIELDS_COUNT;
@@ -229,8 +230,9 @@
     int width = State_width;
     int height = State_height;
 
-    int frameCount = State_frameCount;
-    float now = time(frameCount);
+    float x = lerpf(width, 0, State_xOffset);
+
+    float now = time();
     alpha(1.0f);
 
     if (now >= MIDNIGHT && now < MORNING) {
@@ -251,10 +253,7 @@
         drawSunset(width, height);
     }
 
-    drawBlades(now, frameCount);
-
-    frameCount++;
-    storeI32(RSID_STATE, OFFSETOF_WorldState_frameCount, frameCount);
+    drawBlades(now, x);
 
     return 1;
 }
diff --git a/src/com/android/wallpaper/RenderScriptScene.java b/src/com/android/wallpaper/RenderScriptScene.java
new file mode 100644
index 0000000..01c551c
--- /dev/null
+++ b/src/com/android/wallpaper/RenderScriptScene.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.wallpaper;
+
+import android.content.res.Resources;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptC;
+
+public abstract class RenderScriptScene {
+    protected int mWidth;
+    protected int mHeight;
+    protected Resources mResources;
+    protected RenderScript mRS;
+    protected ScriptC mScript;
+
+    public RenderScriptScene(int width, int height) {
+        mWidth = width;
+        mHeight = height;
+    }
+
+    public void init(RenderScript rs, Resources res) {
+        mRS = rs;
+        mResources = res;
+        mScript = createScript();
+    }
+
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    public Resources getResources() {
+        return mResources;
+    }
+
+    public RenderScript getRS() {
+        return mRS;
+    }
+
+    public ScriptC getScript() {
+        return mScript;
+    }
+
+    protected abstract ScriptC createScript();
+    
+    public void stop() {
+        mRS.contextBindRootScript(null);
+    }
+
+    public void start() {
+        mRS.contextBindRootScript(mScript);
+    }
+
+    public void resize(int width, int height) {
+        mWidth = width;
+        mHeight = height;
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
+    }
+}
diff --git a/src/com/android/wallpaper/RenderScriptWallpaper.java b/src/com/android/wallpaper/RenderScriptWallpaper.java
new file mode 100644
index 0000000..f40bf41
--- /dev/null
+++ b/src/com/android/wallpaper/RenderScriptWallpaper.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.wallpaper;
+
+import android.service.wallpaper.WallpaperService;
+import android.renderscript.RenderScript;
+import android.view.SurfaceHolder;
+import android.view.Surface;
+
+public abstract class RenderScriptWallpaper<T extends RenderScriptScene> extends WallpaperService {
+    public Engine onCreateEngine() {
+        return new RenderScriptEngine();
+    }
+
+    protected abstract T createScene(int width, int height);
+
+    private class RenderScriptEngine extends Engine {
+        private RenderScript mRs;
+        private T mRenderer;
+
+        @Override
+        public void onCreate(SurfaceHolder surfaceHolder) {
+            super.onCreate(surfaceHolder);
+            setTouchEventsEnabled(false);
+            surfaceHolder.setSizeFromLayout();
+        }
+
+        @Override
+        public void onDestroy() {
+            super.onDestroy();
+            destroyRenderer();
+        }
+
+        private void destroyRenderer() {
+            if (mRenderer != null) {
+                mRenderer.stop();
+                mRenderer = null;
+            }
+            if (mRs != null) {
+                mRs.destroy();
+                mRs = null;
+            }            
+        }
+
+        @Override
+        public void onVisibilityChanged(boolean visible) {
+            super.onVisibilityChanged(visible);
+            if (visible) {
+                mRenderer.start();                
+            } else {
+                mRenderer.stop();
+            }
+        }
+
+        @Override
+        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            super.onSurfaceChanged(holder, format, width, height);
+            if (mRenderer == null) {
+                mRenderer = createScene(width, height);
+                mRenderer.init(mRs, getResources());
+                mRenderer.start();
+            } else {
+                mRenderer.resize(width, height);
+            }
+        }
+
+        @Override
+        public void onOffsetsChanged(float xOffset, float yOffset, int xPixels, int yPixels) {
+            mRenderer.setOffset(xOffset, yOffset, xPixels, yPixels);
+        }
+
+        @Override
+        public void onSurfaceCreated(SurfaceHolder holder) {
+            super.onSurfaceCreated(holder);
+
+            Surface surface = null;
+            while (surface == null) {
+                surface = holder.getSurface();
+            }
+            mRs = new RenderScript(surface, false);
+        }
+
+        @Override
+        public void onSurfaceDestroyed(SurfaceHolder holder) {
+            super.onSurfaceDestroyed(holder);
+            destroyRenderer();
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/galaxy/GalaxyRS.java b/src/com/android/wallpaper/galaxy/GalaxyRS.java
index 93d9143..145faf5 100644
--- a/src/com/android/wallpaper/galaxy/GalaxyRS.java
+++ b/src/com/android/wallpaper/galaxy/GalaxyRS.java
@@ -16,8 +16,6 @@
 
 package com.android.wallpaper.galaxy;
 
-import android.content.res.Resources;
-import android.renderscript.RenderScript;
 import android.renderscript.ScriptC;
 import android.renderscript.ProgramFragment;
 import android.renderscript.ProgramStore;
@@ -43,8 +41,9 @@
 import java.util.TimeZone;
 
 import com.android.wallpaper.R;
+import com.android.wallpaper.RenderScriptScene;
 
-class GalaxyRS {
+class GalaxyRS extends RenderScriptScene {
     private static final int GALAXY_RADIUS = 300;
     private static final int PARTICLES_COUNT = 12000;
     private static final float ELLIPSE_TWIST = 0.023333333f;
@@ -67,16 +66,8 @@
 
     private static final int RSID_PARTICLES_BUFFER = 2;
 
-    private Resources mResources;
-    private RenderScript mRS;
-
     private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
 
-    private int mWidth;
-    private int mHeight;
-
-    @SuppressWarnings({"FieldCanBeLocal"})
-    private ScriptC mScript;
     @SuppressWarnings({"FieldCanBeLocal"})
     private ProgramFragment mPfBackground;
     @SuppressWarnings({"FieldCanBeLocal"})
@@ -107,28 +98,14 @@
 
     private final float[] mFloatData5 = new float[5];
 
-    public GalaxyRS(int width, int height) {
-        mWidth = width;
-        mHeight = height;
+    GalaxyRS(int width, int height) {
+        super(width, height);
         mOptionsARGB.inScaled = false;
         mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
     }
 
-    public void init(RenderScript rs, Resources res) {
-        mRS = rs;
-        mResources = res;
-        initRS();
-    }
-
-    void stop() {
-        mRS.contextBindRootScript(null);
-    }
-
-    void start() {
-        mRS.contextBindRootScript(mScript);
-    }
-
-    private void initRS() {
+    @Override
+    protected ScriptC createScript() {
         createProgramVertex();
         createProgramFragmentStore();
         createProgramFragment();
@@ -140,13 +117,15 @@
         sb.setScript(mResources, R.raw.galaxy);
         sb.setRoot(true);
 
-        mScript = sb.create();
-        mScript.setClearColor(1.0f, 0.0f, 0.0f, 1.0f);
-        mScript.setTimeZone(TimeZone.getDefault().getID());
+        ScriptC script = sb.create();
+        script.setClearColor(1.0f, 0.0f, 0.0f, 1.0f);
+        script.setTimeZone(TimeZone.getDefault().getID());
 
-        mScript.bindAllocation(mState, RSID_STATE);
-        mScript.bindAllocation(mParticles, RSID_PARTICLES);
-        mScript.bindAllocation(mParticlesBuffer, RSID_PARTICLES_BUFFER);
+        script.bindAllocation(mState, RSID_STATE);
+        script.bindAllocation(mParticles, RSID_PARTICLES);
+        script.bindAllocation(mParticlesBuffer, RSID_PARTICLES_BUFFER);
+        
+        return script;
     }
 
     private void createScriptStructures() {
@@ -173,14 +152,15 @@
         mParticlesMesh.bindVertexAllocation(mParticlesBuffer, 0);
     }
 
-    void setOffsetX(float xOffset) {
+    @Override
+    public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
         mGalaxyState.xOffset = xOffset;
         mState.data(mGalaxyState);
     }
 
-    void resize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
+    @Override
+    public void resize(int width, int height) {
+        super.resize(width, height);
 
         mGalaxyState.width = width;
         mGalaxyState.height = height;
diff --git a/src/com/android/wallpaper/galaxy/GalaxyWallpaper.java b/src/com/android/wallpaper/galaxy/GalaxyWallpaper.java
index 1dbf09a..1c86eea 100644
--- a/src/com/android/wallpaper/galaxy/GalaxyWallpaper.java
+++ b/src/com/android/wallpaper/galaxy/GalaxyWallpaper.java
@@ -16,85 +16,10 @@
 
 package com.android.wallpaper.galaxy;
 
-import android.service.wallpaper.WallpaperService;
-import android.view.SurfaceHolder;
-import android.view.Surface;
-import android.renderscript.RenderScript;
+import com.android.wallpaper.RenderScriptWallpaper;
 
-public class GalaxyWallpaper extends WallpaperService {
-    public Engine onCreateEngine() {
-        return new RenderScriptEngine();
+public class GalaxyWallpaper extends RenderScriptWallpaper<GalaxyRS> {
+    protected GalaxyRS createScene(int width, int height) {
+        return new GalaxyRS(width, height);
     }
-
-    private class RenderScriptEngine extends Engine {
-        private RenderScript mRs;
-        private GalaxyRS mRenderer;
-
-        @Override
-        public void onCreate(SurfaceHolder surfaceHolder) {
-            super.onCreate(surfaceHolder);
-            surfaceHolder.setSizeFromLayout();
-        }
-
-        @Override
-        public void onDestroy() {
-            super.onDestroy();
-            destroyRenderer();
-        }
-
-        private void destroyRenderer() {
-            if (mRenderer != null) {
-                mRenderer.stop();
-                mRenderer = null;
-            }
-            if (mRs != null) {
-                mRs.destroy();
-                mRs = null;
-            }            
-        }
-
-        @Override
-        public void onVisibilityChanged(boolean visible) {
-            super.onVisibilityChanged(visible);
-            if (visible) {
-                mRenderer.start();
-            } else {
-                mRenderer.stop();
-            }
-        }
-
-        @Override
-        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-            super.onSurfaceChanged(holder, format, width, height);
-            if (mRenderer == null) {
-                mRenderer = new GalaxyRS(width, height);
-                mRenderer.init(mRs, getResources());
-                mRenderer.start();
-            } else {
-                mRenderer.resize(width, height);
-            }
-        }
-
-        @Override
-        public void onOffsetsChanged(float xOffset, float yOffset, int xPixels, int yPixels) {
-            mRenderer.setOffsetX(xOffset);
-        }
-
-        @Override
-        public void onSurfaceCreated(SurfaceHolder holder) {
-            super.onSurfaceCreated(holder);
-
-            Surface surface = null;
-            while (surface == null) {
-                surface = holder.getSurface();
-            }
-            mRs = new RenderScript(surface, false);
-        }
-
-        @Override
-        public void onSurfaceDestroyed(SurfaceHolder holder) {
-            super.onSurfaceDestroyed(holder);
-            destroyRenderer();
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/wallpaper/grass/GrassRS.java b/src/com/android/wallpaper/grass/GrassRS.java
index f16bd31..e249b03 100644
--- a/src/com/android/wallpaper/grass/GrassRS.java
+++ b/src/com/android/wallpaper/grass/GrassRS.java
@@ -16,13 +16,11 @@
 
 package com.android.wallpaper.grass;
 
-import android.content.res.Resources;
 import android.renderscript.Sampler;
 import static android.renderscript.ProgramFragment.EnvMode.*;
 import static android.renderscript.ProgramStore.DepthFunc.*;
 import static android.renderscript.ProgramStore.BlendSrcFunc;
 import static android.renderscript.ProgramStore.BlendDstFunc;
-import android.renderscript.RenderScript;
 import android.renderscript.ProgramFragment;
 import android.renderscript.ProgramStore;
 import android.renderscript.Allocation;
@@ -37,10 +35,11 @@
 import android.renderscript.Primitive;
 import static android.renderscript.Sampler.Value.*;
 import com.android.wallpaper.R;
+import com.android.wallpaper.RenderScriptScene;
 
 import java.util.TimeZone;
 
-class GrassRS {
+class GrassRS extends RenderScriptScene {
     private static final float TESSELATION = 0.5f;
     
     private static final int RSID_STATE = 0;
@@ -66,14 +65,6 @@
 
     private static final int RSID_BLADES_BUFFER = 2;
 
-    private Resources mResources;
-    private RenderScript mRS;
-
-    private int mWidth;
-    private int mHeight;
-
-    @SuppressWarnings({ "FieldCanBeLocal" })
-    private ScriptC mScript;
     @SuppressWarnings({ "FieldCanBeLocal" })
     private ProgramFragment mPfBackground;
     @SuppressWarnings({ "FieldCanBeLocal" })
@@ -100,39 +91,25 @@
     private final float[] mFloatData5 = new float[5];
     private WorldState mWorldState;
 
-    public GrassRS(int width, int height) {
-        mWidth = width;
-        mHeight = height;
+    GrassRS(int width, int height) {
+        super(width, height);
     }
 
-    public void init(RenderScript rs, Resources res) {
-        mRS = rs;
-        mResources = res;
-        initRS();
-    }
-    
-    void stop() {
-        mRS.contextBindRootScript(null);
-    }
-
-    void start() {
-        mRS.contextBindRootScript(mScript);
-    }
-
-    void resize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
-
+    @Override
+    public void resize(int width, int height) {
+        super.resize(width, height);
+        
         mWorldState.width = width;
         mWorldState.height = height;
         mState.data(mWorldState);
 
         mPvOrthoAlloc.setupOrthoWindow(mWidth, mHeight);
 
-        // TODO: REPOSITION BLADES
+        // TODO: REPOSITION BLADES        
     }
-    
-    private void initRS() {
+
+    @Override
+    protected ScriptC createScript() {
         createProgramVertex();
         createProgramFragmentStore();
         createProgramFragment();
@@ -144,13 +121,15 @@
         sb.setScript(mResources, R.raw.grass);
         sb.setRoot(true);
 
-        mScript = sb.create();
-        mScript.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-        mScript.setTimeZone(TimeZone.getDefault().getID());
+        ScriptC script = sb.create();
+        script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        script.setTimeZone(TimeZone.getDefault().getID());
 
-        mScript.bindAllocation(mState, RSID_STATE);
-        mScript.bindAllocation(mBlades, RSID_BLADES);
-        mScript.bindAllocation(mBladesBuffer, RSID_BLADES_BUFFER);
+        script.bindAllocation(mState, RSID_STATE);
+        script.bindAllocation(mBlades, RSID_BLADES);
+        script.bindAllocation(mBladesBuffer, RSID_BLADES_BUFFER);
+        
+        return script;
     }
 
     private void createScriptStructures() {
@@ -158,12 +137,18 @@
         createState();
     }
 
+    @Override
+    public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
+        mWorldState.xOffset = xOffset;
+        mState.data(mWorldState);
+    }
+
     static class WorldState {
-        public int frameCount;
         public int bladesCount;
         public int trianglesCount;
         public int width;
         public int height;
+        public float xOffset;
     }
 
     private void createState() {
@@ -251,7 +236,7 @@
 
     private int createBlade(float[] blades, int index) {
         final float size = random(4.0f) + 4.0f;
-        final int xpos = random(mWidth);
+        final int xpos = random(-mWidth, mWidth);
 
         //noinspection PointlessArithmeticExpression
         blades[index + BLADE_STRUCT_ANGLE] = 0.0f;
diff --git a/src/com/android/wallpaper/grass/GrassWallpaper.java b/src/com/android/wallpaper/grass/GrassWallpaper.java
index 3ee83b1..eabc170 100644
--- a/src/com/android/wallpaper/grass/GrassWallpaper.java
+++ b/src/com/android/wallpaper/grass/GrassWallpaper.java
@@ -16,80 +16,12 @@
 
 package com.android.wallpaper.grass;
 
-import android.service.wallpaper.WallpaperService;
-import android.view.SurfaceHolder;
-import android.view.Surface;
-import android.renderscript.RenderScript;
+import com.android.wallpaper.RenderScriptWallpaper;
+import com.android.wallpaper.RenderScriptScene;
 
-public class GrassWallpaper extends WallpaperService {
-    public Engine onCreateEngine() {
-        return new RenderScriptEngine();
-    }
-
-    private class RenderScriptEngine extends Engine {
-        private RenderScript mRs;
-        private GrassRS mRenderer;
-
-        @Override
-        public void onCreate(SurfaceHolder surfaceHolder) {
-            super.onCreate(surfaceHolder);
-        }
-
-        @Override
-        public void onDestroy() {
-            super.onDestroy();
-            destroyRenderer();
-        }
-
-        private void destroyRenderer() {
-            if (mRenderer != null) {
-                mRenderer.stop();
-                mRenderer = null;
-            }
-            if (mRs != null) {
-                mRs.destroy();
-                mRs = null;
-            }            
-        }
-
-        @Override
-        public void onVisibilityChanged(boolean visible) {
-            super.onVisibilityChanged(visible);
-            if (visible) {
-                mRenderer.start();                
-            } else {
-                mRenderer.stop();
-            }
-        }
-
-        @Override
-        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-            super.onSurfaceChanged(holder, format, width, height);
-            if (mRenderer == null) {
-                mRenderer = new GrassRS(width, height);
-                mRenderer.init(mRs, getResources());
-                mRenderer.start();
-            } else {
-                mRenderer.resize(width, height);
-            }
-        }
-
-        @Override
-        public void onSurfaceCreated(SurfaceHolder holder) {
-            super.onSurfaceCreated(holder);
-
-            Surface surface = null;
-            while (surface == null) {
-                surface = holder.getSurface();
-            }
-            mRs = new RenderScript(surface, false);
-        }
-
-        @Override
-        public void onSurfaceDestroyed(SurfaceHolder holder) {
-            super.onSurfaceDestroyed(holder);
-            destroyRenderer();
-        }
+public class GrassWallpaper extends RenderScriptWallpaper {
+    protected RenderScriptScene createScene(int width, int height) {
+        return new GrassRS(width, height);
     }
 }