Merge "Fix issue #5461497: Landed on Welcome screen after a tap on 'Next' in Screen 500" into ics-mr0
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7eae739..081e267 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -108,6 +108,7 @@
     private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
     private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
     private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
+    private static final boolean DEBUG_FPS = false;
     private static final boolean WATCH_POINTER = false;
 
     /**
@@ -274,6 +275,11 @@
     private Thread mRenderProfiler;
     private volatile boolean mRenderProfilingEnabled;
 
+    // Variables to track frames per second, enabled via DEBUG_FPS flag
+    private long mFpsStartTime = -1;
+    private long mFpsPrevTime = -1;
+    private int mFpsNumFrames;
+
     /**
      * see {@link #playSoundEffect(int)}
      */
@@ -1766,12 +1772,42 @@
         }
     }
 
+    /**
+     * Called from draw() when DEBUG_FPS is enabled
+     */
+    private void trackFPS() {
+        // Tracks frames per second drawn. First value in a series of draws may be bogus
+        // because it down not account for the intervening idle time
+        long nowTime = System.currentTimeMillis();
+        if (mFpsStartTime < 0) {
+            mFpsStartTime = mFpsPrevTime = nowTime;
+            mFpsNumFrames = 0;
+        } else {
+            ++mFpsNumFrames;
+            String thisHash = Integer.toHexString(System.identityHashCode(this));
+            long frameTime = nowTime - mFpsPrevTime;
+            long totalTime = nowTime - mFpsStartTime;
+            Log.v(TAG, "0x" + thisHash + "\tFrame time:\t" + frameTime);
+            mFpsPrevTime = nowTime;
+            if (totalTime > 1000) {
+                float fps = (float) mFpsNumFrames * 1000 / totalTime;
+                Log.v(TAG, "0x" + thisHash + "\tFPS:\t" + fps);
+                mFpsStartTime = nowTime;
+                mFpsNumFrames = 0;
+            }
+        }
+    }
+
     private void draw(boolean fullRedrawNeeded) {
         Surface surface = mSurface;
         if (surface == null || !surface.isValid()) {
             return;
         }
 
+        if (DEBUG_FPS) {
+            trackFPS();
+        }
+
         if (!sFirstDrawComplete) {
             synchronized (sFirstDrawHandlers) {
                 sFirstDrawComplete = true;
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 07281d4..14ce266 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -17,6 +17,7 @@
 package com.android.systemui;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.ValueAnimator;
@@ -40,6 +41,8 @@
     public static final int X = 0;
     public static final int Y = 1;
 
+    private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
     private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
@@ -199,6 +202,10 @@
         return mDragging;
     }
 
+    /**
+     * @param view The view to be dismissed
+     * @param velocity The desired pixels/second speed at which the view should move
+     */
     public void dismissChild(final View view, float velocity) {
         final View animView = mCallback.getChildContentView(view);
         final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
@@ -221,22 +228,14 @@
             duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
         }
 
+        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         ObjectAnimator anim = createTranslationAnimation(animView, newPos);
-        anim.setInterpolator(new LinearInterpolator());
+        anim.setInterpolator(sLinearInterpolator);
         anim.setDuration(duration);
-        anim.addListener(new AnimatorListener() {
-            public void onAnimationStart(Animator animation) {
-            }
-
-            public void onAnimationRepeat(Animator animation) {
-            }
-
+        anim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
                 mCallback.onChildDismissed(view);
-            }
-
-            public void onAnimationCancel(Animator animation) {
-                mCallback.onChildDismissed(view);
+                animView.setLayerType(View.LAYER_TYPE_NONE, null);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 4e9b411..b724552 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -50,7 +50,6 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.Surface;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup;
@@ -213,6 +212,8 @@
     boolean mAnimatingReveal = false;
     int mViewDelta;
     int[] mAbsPos = new int[2];
+    Runnable mPostCollapseCleanup = null;
+
 
     // for disabling the status bar
     int mDisabled = 0;
@@ -1238,6 +1239,10 @@
             return;
         }
         mExpanded = false;
+        if (mPostCollapseCleanup != null) {
+            mPostCollapseCleanup.run();
+            mPostCollapseCleanup = null;
+        }
     }
 
     void doAnimation() {
@@ -2066,49 +2071,67 @@
         }
         public void onClick(View v) {
             synchronized (mNotificationData) {
-                // let's also queue up 400ms worth of animated dismissals
-                final int N = mini(5, mPile.getChildCount());
+                // animate-swipe all dismissable notifications, then animate the shade closed
+                int numChildren = mPile.getChildCount();
 
-                final ArrayList<View> snapshot = new ArrayList<View>(N);
-                for (int i=0; i<N; i++) {
+                int scrollTop = mScrollView.getScrollY();
+                int scrollBottom = scrollTop + mScrollView.getHeight();
+                final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
+                for (int i=0; i<numChildren; i++) {
                     final View child = mPile.getChildAt(i);
-                    if (mPile.canChildBeDismissed(child)) snapshot.add(child);
+                    if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
+                            child.getTop() < scrollBottom) {
+                        snapshot.add(child);
+                    }
                 }
+                final int N = snapshot.size();
                 new Thread(new Runnable() {
                     @Override
                     public void run() {
-                        final int ROW_DELAY = 100;
+                        // Decrease the delay for every row we animate to give the sense of
+                        // accelerating the swipes
+                        final int ROW_DELAY_DECREMENT = 10;
+                        int currentDelay = 140;
+                        int totalDelay = 0;
 
-                        mHandler.postDelayed(new Runnable() {
-                            public void run() {
-                                animateCollapse(false, 0f);
-                            }
-                        }, (N-1) * ROW_DELAY);
+                        // Set the shade-animating state to avoid doing other work during
+                        // all of these animations. In particular, avoid layout and
+                        // redrawing when collapsing the shade.
+                        mPile.setViewRemoval(false);
 
-                        mHandler.postDelayed(new Runnable() {
+                        mPostCollapseCleanup = new Runnable() {
                             public void run() {
                                 try {
+                                    mPile.setViewRemoval(true);
                                     mBarService.onClearAllNotifications();
-                                } catch (RemoteException ex) { }
+                                } catch (Exception ex) { }
                             }
-                        }, N * ROW_DELAY + 500);
+                        };
 
-                        mPile.setAnimateBounds(false); // temporarily disable some re-layouts
-
+                        View sampleView = snapshot.get(0);
+                        int width = sampleView.getWidth();
+                        final int velocity = (int)(width * 8); // 1000/8 = 125 ms duration
                         for (View v : snapshot) {
                             final View _v = v;
-                            mHandler.post(new Runnable() {
+                            mHandler.postDelayed(new Runnable() {
                                 @Override
                                 public void run() {
-                                    mPile.dismissRowAnimated(_v, (int)(ROW_DELAY*0.25f));
+                                    mPile.dismissRowAnimated(_v, velocity);
                                 }
-                            });
-                            try {
-                                Thread.sleep(ROW_DELAY);
-                            } catch (InterruptedException ex) { }
+                            }, totalDelay);
+                            currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
+                            totalDelay += currentDelay;
                         }
-                        
-                        mPile.setAnimateBounds(true); // reenable layout animation
+                        // Delay the collapse animation until after all swipe animations have
+                        // finished. Provide some buffer because there may be some extra delay
+                        // before actually starting each swipe animation. Ideally, we'd
+                        // synchronize the end of those animations with the start of the collaps
+                        // exactly.
+                        mHandler.postDelayed(new Runnable() {
+                            public void run() {
+                                animateCollapse(false);
+                            }
+                        }, totalDelay + 225);
                     }
                 }).start();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index a7342dc..3649f75e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -29,7 +28,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
@@ -59,6 +57,10 @@
 
     private SwipeHelper mSwipeHelper;
 
+    // Flag set during notification removal animation to avoid causing too much work until
+    // animation is done
+    boolean mRemoveViews = true;
+
     public NotificationRowLayout(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -117,7 +119,7 @@
 
     public void onChildDismissed(View v) {
         final View veto = v.findViewById(R.id.veto);
-        if (veto != null && veto.getVisibility() != View.GONE) {
+        if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) {
             veto.performClick();
         }
     }
@@ -170,7 +172,6 @@
         final View childF = child;
 
         if (mAnimateBounds) {
-            child.setPivotY(0);
             final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
             alphaFade.setDuration(APPEAR_ANIM_LEN);
             alphaFade.addListener(new AnimatorListenerAdapter() {
@@ -189,6 +190,16 @@
         }
     }
 
+    /**
+     * Sets a flag to tell us whether to actually remove views. Removal is delayed by setting this
+     * to false during some animations to smooth out performance. Callers should restore the
+     * flag to true after the animation is done, and then they should make sure that the views
+     * get removed properly.
+     */
+    public void setViewRemoval(boolean removeViews) {
+        mRemoveViews = removeViews;
+    }
+
     public void dismissRowAnimated(View child) {
         dismissRowAnimated(child, 0);
     }
@@ -199,16 +210,34 @@
 
     @Override
     public void removeView(View child) {
-        final View childF = child;
+        if (!mRemoveViews) {
+            // This flag is cleared during an animation that removes all notifications. There
+            // should be a call to remove all notifications when the animation is done, at which
+            // time the view will be removed.
+            return;
+        }
         if (mAnimateBounds) {
             if (mAppearingViews.containsKey(child)) {
                 mAppearingViews.remove(child);
             }
-            child.setPivotY(0);
 
-            final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f);
-            alphaFade.setDuration(DISAPPEAR_ANIM_LEN);
-            alphaFade.addListener(new AnimatorListenerAdapter() {
+            // Don't fade it out if it already has a low alpha value, but run a non-visual
+            // animation which is used by onLayout() to animate shrinking the gap that it left
+            // in the list
+            ValueAnimator anim;
+            float currentAlpha = child.getAlpha();
+            if (currentAlpha > .1) {
+                anim = ObjectAnimator.ofFloat(child, "alpha", currentAlpha, 0);
+            } else {
+                if (currentAlpha > 0) {
+                    // Just make it go away - no need to render it anymore
+                    child.setAlpha(0);
+                }
+                anim = ValueAnimator.ofFloat(0, 1);
+            }
+            anim.setDuration(DISAPPEAR_ANIM_LEN);
+            final View childF = child;
+            anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     if (DEBUG) Slog.d(TAG, "actually removing child: " + childF);
@@ -218,9 +247,8 @@
                 }
             });
 
-            alphaFade.start();
-
-            mDisappearingViews.put(child, alphaFade);
+            anim.start();
+            mDisappearingViews.put(child, anim);
 
             requestLayout(); // start the container animation
         } else {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index ac3cdb8..e166401 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -273,7 +273,14 @@
                 break;
             case ConnectivityManager.TYPE_MOBILE_HIPRI:
                 apnContext = addApnContext(Phone.APN_TYPE_HIPRI);
-                break;
+                ApnContext defaultContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
+                if (defaultContext != null) {
+                    applyNewState(apnContext, apnContext.isEnabled(),
+                            defaultContext.getDependencyMet());
+                } else {
+                    // the default will set the hipri dep-met when it is created
+                }
+                continue;
             case ConnectivityManager.TYPE_MOBILE_FOTA:
                 apnContext = addApnContext(Phone.APN_TYPE_FOTA);
                 break;