All (er, L) is finally revealed.

If you exceed 20000 points, please send in a screenshot.

Bug: 15984887
Change-Id: I1164160bfa8e7dca3b9e7d7ca5d50ef9abd38b61
diff --git a/packages/SystemUI/src/com/android/systemui/egg/LLand.java b/packages/SystemUI/src/com/android/systemui/egg/LLand.java
index d1c02dd..cdfe6e5 100644
--- a/packages/SystemUI/src/com/android/systemui/egg/LLand.java
+++ b/packages/SystemUI/src/com/android/systemui/egg/LLand.java
@@ -19,26 +19,37 @@
 import android.animation.TimeAnimator;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.*;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.*;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewOutlineProvider;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import java.util.ArrayList;
-
 import com.android.systemui.R;
 
+import java.util.ArrayList;
+
 public class LLand extends FrameLayout {
     public static final String TAG = "LLand";
 
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    public static final boolean DEBUG_DRAW = false && DEBUG;
+    public static final boolean DEBUG_DRAW = false; // DEBUG
 
     public static final void L(String s, Object ... objects) {
         if (DEBUG) {
@@ -52,13 +63,24 @@
     public static final float DEBUG_SPEED_MULTIPLIER = 1f; // 0.1f;
     public static final boolean DEBUG_IDDQD = false;
 
+    final static int[] POPS = {
+            // resid                // spinny!
+            R.drawable.pop_belt,    0,
+            R.drawable.pop_droid,   0,
+            R.drawable.pop_pizza,   1,
+            R.drawable.pop_stripes, 0,
+            R.drawable.pop_swirl,   1,
+            R.drawable.pop_vortex,  1,
+            R.drawable.pop_vortex2, 1,
+    };
+
     private static class Params {
         public float TRANSLATION_PER_SEC;
         public int OBSTACLE_SPACING, OBSTACLE_PERIOD;
         public int BOOST_DV;
         public int PLAYER_HIT_SIZE;
         public int PLAYER_SIZE;
-        public int OBSTACLE_WIDTH;
+        public int OBSTACLE_WIDTH, OBSTACLE_STEM_WIDTH;
         public int OBSTACLE_GAP;
         public int OBSTACLE_MIN;
         public int BUILDING_WIDTH_MIN, BUILDING_WIDTH_MAX;
@@ -76,6 +98,7 @@
             PLAYER_HIT_SIZE = res.getDimensionPixelSize(R.dimen.player_hit_size);
             PLAYER_SIZE = res.getDimensionPixelSize(R.dimen.player_size);
             OBSTACLE_WIDTH = res.getDimensionPixelSize(R.dimen.obstacle_width);
+            OBSTACLE_STEM_WIDTH = res.getDimensionPixelSize(R.dimen.obstacle_stem_width);
             OBSTACLE_GAP = res.getDimensionPixelSize(R.dimen.obstacle_gap);
             OBSTACLE_MIN = res.getDimensionPixelSize(R.dimen.obstacle_height_min);
             BUILDING_HEIGHT_MIN = res.getDimensionPixelSize(R.dimen.building_height_min);
@@ -112,6 +135,7 @@
     private int mWidth, mHeight;
     private boolean mAnimating, mPlaying;
     private boolean mFrozen; // after death, a short backoff
+    private boolean mFlipped;
 
     private int mTimeOfDay;
     private static final int DAY = 0, NIGHT = 1, TWILIGHT = 2, SUNSET = 3;
@@ -119,7 +143,7 @@
             { 0xFFc0c0FF, 0xFFa0a0FF }, // DAY
             { 0xFF000010, 0xFF000000 }, // NIGHT
             { 0xFF000040, 0xFF000010 }, // TWILIGHT
-            { 0xFF805010, 0xFF202080 }, // SUNSET
+            { 0xFFa08020, 0xFF204080 }, // SUNSET
     };
 
     private static Params PARAMS;
@@ -183,7 +207,8 @@
         sky.setDither(true);
         setBackground(sky);
 
-        setScaleX(frand() > 0.5f ? 1 : -1);
+        mFlipped = frand() > 0.5f;
+        setScaleX(mFlipped ? -1 : 1);
 
         setScore(0);
 
@@ -276,9 +301,7 @@
         mDroid.setX(mWidth / 2);
         mDroid.setY(mHeight / 2);
         addView(mDroid, new LayoutParams(PARAMS.PLAYER_SIZE, PARAMS.PLAYER_SIZE));
-        if (mAnim != null) {
-            Log.wtf(TAG, "reseting while animating??!?");
-        }
+
         mAnim = new TimeAnimator();
         mAnim.setTimeListener(new TimeAnimator.TimeListener() {
             @Override
@@ -303,7 +326,8 @@
             mPlaying = true;
 
             t = 0;
-            mLastPipeTime = getGameTime() - PARAMS.OBSTACLE_PERIOD; // queue up a obstacle
+            // there's a sucker born every OBSTACLE_PERIOD
+            mLastPipeTime = getGameTime() - PARAMS.OBSTACLE_PERIOD;
 
             if (mSplash != null && mSplash.getAlpha() > 0f) {
                 mSplash.setTranslationZ(PARAMS.HUD_Z);
@@ -407,7 +431,7 @@
                 L("player hit an obstacle");
                 stop();
             } else if (ob.cleared(mDroid)) {
-                passedBarrier = true;
+                if (ob instanceof Stem) passedBarrier = true;
                 mObstaclesInPlay.remove(j);
             }
         }
@@ -438,52 +462,91 @@
             final int obstacley = (int) (Math.random()
                     * (mHeight - 2*PARAMS.OBSTACLE_MIN - PARAMS.OBSTACLE_GAP)) + PARAMS.OBSTACLE_MIN;
 
-            final Obstacle p1 = new Obstacle(getContext(), obstacley);
+            final int inset = (PARAMS.OBSTACLE_WIDTH - PARAMS.OBSTACLE_STEM_WIDTH) / 2;
+            final int yinset = PARAMS.OBSTACLE_WIDTH/2;
+
+            final int d1 = irand(0,250);
+            final Obstacle s1 = new Stem(getContext(), obstacley - yinset, false);
+            addView(s1, new LayoutParams(
+                    PARAMS.OBSTACLE_STEM_WIDTH,
+                    (int) s1.h,
+                    Gravity.TOP|Gravity.LEFT));
+            s1.setTranslationX(mWidth+inset);
+            s1.setTranslationY(-s1.h-yinset);
+            s1.setTranslationZ(PARAMS.OBSTACLE_Z*0.75f);
+            s1.animate()
+                    .translationY(0)
+                    .setStartDelay(d1)
+                    .setDuration(250);
+            mObstaclesInPlay.add(s1);
+
+            final Obstacle p1 = new Pop(getContext(), PARAMS.OBSTACLE_WIDTH);
             addView(p1, new LayoutParams(
                     PARAMS.OBSTACLE_WIDTH,
-                    mHeight,
+                    PARAMS.OBSTACLE_WIDTH,
                     Gravity.TOP|Gravity.LEFT));
             p1.setTranslationX(mWidth);
-            p1.setTranslationY(-mHeight);
-            p1.setTranslationZ(0);
+            p1.setTranslationY(-PARAMS.OBSTACLE_WIDTH);
+            p1.setTranslationZ(PARAMS.OBSTACLE_Z);
+            p1.setScaleX(0.25f);
+            p1.setScaleY(0.25f);
             p1.animate()
-                    .translationY(-mHeight+p1.h)
-                    .translationZ(PARAMS.OBSTACLE_Z)
-                    .setStartDelay(irand(0,250))
+                    .translationY(s1.h-inset)
+                    .scaleX(1f)
+                    .scaleY(1f)
+                    .setStartDelay(d1)
                     .setDuration(250);
             mObstaclesInPlay.add(p1);
 
-            final Obstacle p2 = new Obstacle(getContext(),
-                    mHeight - obstacley - PARAMS.OBSTACLE_GAP);
+            final int d2 = irand(0,250);
+            final Obstacle s2 = new Stem(getContext(),
+                    mHeight - obstacley - PARAMS.OBSTACLE_GAP - yinset,
+                    true);
+            addView(s2, new LayoutParams(
+                    PARAMS.OBSTACLE_STEM_WIDTH,
+                    (int) s2.h,
+                    Gravity.TOP|Gravity.LEFT));
+            s2.setTranslationX(mWidth+inset);
+            s2.setTranslationY(mHeight+yinset);
+            s2.setTranslationZ(PARAMS.OBSTACLE_Z*0.75f);
+            s2.animate()
+                    .translationY(mHeight-s2.h)
+                    .setStartDelay(d2)
+                    .setDuration(400);
+            mObstaclesInPlay.add(s2);
+
+            final Obstacle p2 = new Pop(getContext(), PARAMS.OBSTACLE_WIDTH);
             addView(p2, new LayoutParams(
                     PARAMS.OBSTACLE_WIDTH,
-                    mHeight,
+                    PARAMS.OBSTACLE_WIDTH,
                     Gravity.TOP|Gravity.LEFT));
             p2.setTranslationX(mWidth);
             p2.setTranslationY(mHeight);
-            p2.setTranslationZ(0);
+            p2.setTranslationZ(PARAMS.OBSTACLE_Z);
+            p2.setScaleX(0.25f);
+            p2.setScaleY(0.25f);
             p2.animate()
-                    .translationY(mHeight-p2.h)
-                    .translationZ(PARAMS.OBSTACLE_Z)
-                    .setStartDelay(irand(0,100))
+                    .translationY(mHeight-s2.h-yinset)
+                    .scaleX(1f)
+                    .scaleY(1f)
+                    .setStartDelay(d2)
                     .setDuration(400);
             mObstaclesInPlay.add(p2);
         }
 
-        if (DEBUG) {
-            final Rect r = new Rect();
-            mDroid.getHitRect(r);
-            r.inset(-4, -4);
-            invalidate(r);
-        }
+        if (DEBUG_DRAW) invalidate();
     }
     
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (DEBUG) L("touch: %s", ev);
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            poke();
-            return true;
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                poke();
+                return true;
+            case MotionEvent.ACTION_UP:
+                unpoke();
+                return true;
         }
         return false;
     }
@@ -491,9 +554,13 @@
     @Override
     public boolean onTrackballEvent(MotionEvent ev) {
         if (DEBUG) L("trackball: %s", ev);
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            poke();
-            return true;
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                poke();
+                return true;
+            case MotionEvent.ACTION_UP:
+                unpoke();
+                return true;
         }
         return false;
     }
@@ -514,6 +581,21 @@
     }
 
     @Override
+    public boolean onKeyUp(int keyCode, KeyEvent ev) {
+        if (DEBUG) L("keyDown: %d", keyCode);
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_SPACE:
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_BUTTON_A:
+                unpoke();
+                return true;
+        }
+        return false;
+    }
+
+    @Override
     public boolean onGenericMotionEvent (MotionEvent ev) {
         if (DEBUG) L("generic: %s", ev);
         return false;
@@ -535,6 +617,13 @@
         }
     }
 
+    private void unpoke() {
+        L("unboost");
+        if (mFrozen) return;
+        if (!mAnimating) return;
+        mDroid.unboost();
+    }
+
     @Override
     public void onDraw(Canvas c) {
         super.onDraw(c);
@@ -555,15 +644,23 @@
                     pt);
         }
 
+        pt.setStyle(Paint.Style.STROKE);
+        pt.setStrokeWidth(getResources().getDisplayMetrics().density);
+
         final int M = getChildCount();
-        pt.setColor(0x6000FF00);
+        pt.setColor(0x8000FF00);
         for (int i=0; i<M; i++) {
             final View v = getChildAt(i);
             if (v == mDroid) continue;
             if (!(v instanceof GameView)) continue;
-            final Rect r = new Rect();
-            v.getHitRect(r);
-            c.drawRect(r, pt);
+            if (v instanceof Pop) {
+                final Pop p = (Pop) v;
+                c.drawCircle(p.cx, p.cy, p.r, pt);
+            } else {
+                final Rect r = new Rect();
+                v.getHitRect(r);
+                c.drawRect(r, pt);
+            }
         }
 
         pt.setColor(Color.BLACK);
@@ -585,6 +682,8 @@
     private class Player extends ImageView implements GameView {
         public float dv;
 
+        private boolean mBoosting;
+
         private final float[] sHull = new float[] {
                 0.3f,  0f,    // left antenna
                 0.7f,  0f,    // right antenna
@@ -639,7 +738,11 @@
         public void step(long t_ms, long dt_ms, float t, float dt) {
             if (getVisibility() != View.VISIBLE) return; // not playing yet
 
-            dv += PARAMS.G;
+            if (mBoosting) {
+                dv = -PARAMS.BOOST_DV;
+            } else {
+                dv += PARAMS.G;
+            }
             if (dv < -PARAMS.MAX_V) dv = -PARAMS.MAX_V;
             else if (dv > PARAMS.MAX_V) dv = PARAMS.MAX_V;
 
@@ -652,15 +755,28 @@
         }
 
         public void boost() {
+            mBoosting = true;
             dv = -PARAMS.BOOST_DV;
-            setTranslationZ(PARAMS.PLAYER_Z_BOOST);
+
+            animate().cancel();
+            animate()
+                    .scaleX(1.25f)
+                    .scaleY(1.25f)
+                    .translationZ(PARAMS.PLAYER_Z_BOOST)
+                    .setDuration(100);
             setScaleX(1.25f);
             setScaleY(1.25f);
+        }
+
+        public void unboost() {
+            mBoosting = false;
+
+            animate().cancel();
             animate()
-                .scaleX(1f)
-                .scaleY(1f)
-                .translationZ(PARAMS.PLAYER_Z)
-                .setDuration(200);
+                    .scaleX(1f)
+                    .scaleY(1f)
+                    .translationZ(PARAMS.PLAYER_Z)
+                    .setDuration(200);
         }
     }
 
@@ -671,7 +787,7 @@
 
         public Obstacle(Context context, float h) {
             super(context);
-            setBackgroundResource(R.drawable.placeholder);
+            setBackgroundColor(0xFFFF0000);
             this.h = h;
         }
 
@@ -701,6 +817,91 @@
         }
     }
 
+    private class Pop extends Obstacle {
+        int mRotate;
+        int cx, cy, r;
+        public Pop(Context context, float h) {
+            super(context, h);
+            int idx = 2*irand(0, POPS.length/2);
+            setBackgroundResource(POPS[idx]);
+            setScaleX(frand() < 0.5f ? -1 : 1);
+            mRotate = POPS[idx+1] == 0 ? 0 : (frand() < 0.5f ? -1 : 1);
+            setOutlineProvider(new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View view, Outline outline) {
+                    final int pad = (int) (getWidth() * 0.02f);
+                    outline.setOval(pad, pad, getWidth()-pad, getHeight()-pad);
+                }
+            });
+        }
+
+        public boolean intersects(Player p) {
+            final int N = p.corners.length/2;
+            for (int i=0; i<N; i++) {
+                final int x = (int) p.corners[i*2];
+                final int y = (int) p.corners[i*2+1];
+                if (Math.hypot(x-cx, y-cy) <= r) return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void step(long t_ms, long dt_ms, float t, float dt) {
+            super.step(t_ms, dt_ms, t, dt);
+            if (mRotate != 0) {
+                setRotation(getRotation() + dt * 45 * mRotate);
+            }
+
+            cx = (hitRect.left + hitRect.right)/2;
+            cy = (hitRect.top + hitRect.bottom)/2;
+            r = getWidth()/2;
+        }
+    }
+
+    private class Stem extends Obstacle {
+        Paint mPaint = new Paint();
+        Path mShadow = new Path();
+        boolean mDrawShadow;
+
+        public Stem(Context context, float h, boolean drawShadow) {
+            super(context, h);
+            mDrawShadow = drawShadow;
+            mPaint.setColor(0xFFAAAAAA);
+            setBackground(null);
+        }
+
+        @Override
+        public void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            setWillNotDraw(false);
+            setOutlineProvider(new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View view, Outline outline) {
+                    outline.setRect(0, 0, getWidth(), getHeight());
+                }
+            });
+        }
+        @Override
+        public void onDraw(Canvas c) {
+            final int w = c.getWidth();
+            final int h = c.getHeight();
+            final GradientDrawable g = new GradientDrawable();
+            g.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT);
+            g.setGradientCenter(w * 0.75f, 0);
+            g.setColors(new int[] { 0xFFFFFFFF, 0xFFAAAAAA });
+            g.setBounds(0, 0, w, h);
+            g.draw(c);
+            if (!mDrawShadow) return;
+            mShadow.reset();
+            mShadow.moveTo(0,0);
+            mShadow.lineTo(w, 0);
+            mShadow.lineTo(w, PARAMS.OBSTACLE_WIDTH/2+w*1.5f);
+            mShadow.lineTo(0, PARAMS.OBSTACLE_WIDTH/2);
+            mShadow.close();
+            c.drawPath(mShadow, mPaint);
+        }
+    }
+
     private class Scenery extends FrameLayout implements GameView {
         public float z;
         public float v;