Fixing issues with dim not animating, adding dismiss button delay, tweaking empty recents text. (Bug 15431590, 15573497)

Change-Id: I9c11e04cd52f9e000d8ba3e443f2500c9aa7edbb
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
index ac6450b..c0ddde1 100644
--- a/packages/SystemUI/res/layout/recents_empty.xml
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -21,7 +21,6 @@
     android:gravity="center"
     android:textSize="20sp"
     android:textColor="#ffffffff"
-    android:textStyle="italic"
     android:text="@string/recents_empty_message"
     android:fontFamily="sans-serif-light"
     android:visibility="gone" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 85d2f16..23f2796 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -56,6 +56,7 @@
             android:layout_marginEnd="4dp"
             android:layout_gravity="center_vertical|end"
             android:padding="18dp"
+            android:visibility="invisible"
             android:src="@drawable/recents_dismiss_light" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b39fa84..09a94f5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -108,6 +108,8 @@
     <!-- milliseconds before the heads up notification accepts touches. -->
     <integer name="heads_up_sensitivity_delay">700</integer>
 
+    <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
+    <integer name="recents_task_bar_dismiss_delay_seconds">3</integer>
     <!-- The min animation duration for animating views that are currently visible. -->
     <integer name="recents_filter_animate_current_views_min_duration">175</integer>
     <!-- The min animation duration for animating views that are newly visible. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
new file mode 100644
index 0000000..247aa6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents;
+
+import android.os.Handler;
+
+/**
+ * A dozer is a class that fires a trigger after it falls asleep.  You can occasionally poke it to
+ * wake it up, but it will fall asleep if left untouched.
+ */
+public class DozeTrigger {
+
+    Handler mHandler;
+
+    boolean mIsDozing;
+    boolean mHasTriggered;
+    int mDozeDurationSeconds;
+    Runnable mSleepRunnable;
+
+    // Sleep-runnable
+    Runnable mDozeRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mSleepRunnable.run();
+            mIsDozing = false;
+            mHasTriggered = true;
+        }
+    };
+
+    public DozeTrigger(int dozeDurationSeconds, Runnable sleepRunnable) {
+        mHandler = new Handler();
+        mDozeDurationSeconds = dozeDurationSeconds;
+        mSleepRunnable = sleepRunnable;
+    }
+
+    /** Starts dozing. This also resets the trigger flag. */
+    public void startDozing() {
+        poke();
+        mHasTriggered = false;
+    }
+
+    /** Poke this dozer to wake it up for a little bit. */
+    public void poke() {
+        mHandler.removeCallbacks(mDozeRunnable);
+        mHandler.postDelayed(mDozeRunnable, mDozeDurationSeconds * 1000);
+        mIsDozing = true;
+    }
+
+    /** Returns whether we are dozing or not. */
+    public boolean isDozing() {
+        return mIsDozing;
+    }
+
+    /** Returns whether the trigger has fired at least once. */
+    public boolean hasTriggered() {
+        return mHasTriggered;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index f9c219b..05c3252 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -527,6 +527,11 @@
     }
 
     @Override
+    public void onUserInteraction() {
+        mRecentsView.onUserInteraction();
+    }
+
+    @Override
     public void onBackPressed() {
         // If we are mid-animation into Recents, then reverse it and finish
         if (mFullScreenshotView == null ||
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index a0c5253..b1f3733 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -89,6 +89,7 @@
     public int taskBarEnterAnimDuration;
     public int taskBarEnterAnimDelay;
     public int taskBarExitAnimDuration;
+    public int taskBarDismissDozeDelaySeconds;
 
     /** Nav bar scrim */
     public int navBarScrimEnterDuration;
@@ -205,6 +206,8 @@
                 res.getInteger(R.integer.recents_animate_task_bar_enter_delay);
         taskBarExitAnimDuration =
                 res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
+        taskBarDismissDozeDelaySeconds =
+                res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
 
         // Nav bar scrim
         navBarScrimEnterDuration =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index a2c250c..d019037 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -307,6 +307,20 @@
         }
     }
 
+    /** Notifies each task view of the user interaction. */
+    public void onUserInteraction() {
+        // Get the first stack view
+        TaskStackView stackView = null;
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child instanceof TaskStackView) {
+                stackView = (TaskStackView) child;
+                stackView.onUserInteraction();
+            }
+        }
+    }
+
     /** Focuses the next task in the first stack view */
     public void focusNextTask(boolean forward) {
         // Get the first stack view
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 2c637a8..aae345b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -105,7 +105,7 @@
     /** Synchronizes this bar view's properties with the task's transform */
     void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
                                              TaskViewTransform toTransform, int duration) {
-        if (duration > 0) {
+        if (duration > 0 && (mDismissButton.getVisibility() == View.VISIBLE)) {
             if (animateFromTransform != null) {
                 mDismissButton.setAlpha(animateFromTransform.dismissAlpha);
             }
@@ -169,7 +169,7 @@
         setTranslationY(-getMeasuredHeight());
         animate()
                 .translationY(0)
-                .setStartDelay(delay > -1 ? delay : mConfig.taskBarEnterAnimDelay)
+                .setStartDelay(delay)
                 .setInterpolator(mConfig.fastOutSlowInInterpolator)
                 .setDuration(mConfig.taskBarEnterAnimDuration)
                 .withLayer()
@@ -178,6 +178,7 @@
 
     /** Animates this task bar as it exits recents */
     public void animateOnLaunchingTask(final Runnable r) {
+        // Animate the task bar out of the first task view
         animate()
                 .translationY(-getMeasuredHeight())
                 .setStartDelay(0)
@@ -193,6 +194,28 @@
                 .start();
     }
 
+    /** Animates this task bar if the user does not interact with the stack after a certain time. */
+    public void animateOnNoUserInteraction() {
+        mDismissButton.setVisibility(View.VISIBLE);
+        mDismissButton.setAlpha(0f);
+        mDismissButton.animate()
+                .alpha(1f)
+                .setStartDelay(0)
+                .setInterpolator(mConfig.fastOutLinearInInterpolator)
+                .setDuration(mConfig.taskBarEnterAnimDuration)
+                .withLayer()
+                .start();
+    }
+
+    /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+    public void setOnNoUserInteraction() {
+        if (mDismissButton.getVisibility() != View.VISIBLE) {
+            mDismissButton.animate().cancel();
+            mDismissButton.setVisibility(View.VISIBLE);
+            mDismissButton.setAlpha(1f);
+        }
+    }
+
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
         mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 186565b..b503c36 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -38,6 +38,7 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.DozeTrigger;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.RecentsPackageMonitor;
 import com.android.systemui.recents.RecentsTaskLoader;
@@ -70,6 +71,7 @@
     TaskStackViewCallbacks mCb;
     ViewPool<TaskView, Task> mViewPool;
     ArrayList<TaskViewTransform> mTaskTransforms = new ArrayList<TaskViewTransform>();
+    DozeTrigger mDozeTrigger;
 
     // The various rects that define the stack view
     Rect mRect = new Rect();
@@ -107,6 +109,17 @@
         mTouchHandler = new TaskStackViewTouchHandler(context, this);
         mViewPool = new ViewPool<TaskView, Task>(context, this);
         mInflater = LayoutInflater.from(context);
+        mDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
+            @Override
+            public void run() {
+                // Show the task bar dismiss buttons
+                int childCount = getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    TaskView tv = (TaskView) getChildAt(i);
+                    tv.animateOnNoUserInteraction();
+                }
+            }
+        });
         mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() {
             @Override
             public void run() {
@@ -797,6 +810,9 @@
             // Mark that we have completely the first layout
             mAwaitingFirstLayout = false;
 
+            // Start dozing
+            mDozeTrigger.startDozing();
+
             // Prepare the first view for its enter animation
             int offsetTopAlign = -mTaskRect.top;
             int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top);
@@ -867,6 +883,15 @@
         return isTransformedTouchPointInView(x, y, child, null);
     }
 
+    /** Pokes the dozer on user interaction. */
+    void onUserInteraction() {
+        // If the dozer is not running, then either we have not yet laid out, or it has already
+        // fallen asleep, so just let it rest.
+        if (mDozeTrigger.isDozing()) {
+            mDozeTrigger.poke();
+        }
+    }
+
     /**** TaskStackCallbacks Implementation ****/
 
     @Override
@@ -1152,6 +1177,11 @@
         // but just in case, re-enable it here
         tv.setClipViewInStack(true);
 
+        // If the doze trigger has already fired, then update the state for this task view
+        if (mDozeTrigger.hasTriggered()) {
+            tv.setOnNoUserInteraction();
+        }
+
         // Add/attach the view to the hierarchy
         if (Console.Enabled) {
             Console.log(Constants.Log.ViewPool.PoolCallbacks, "  [TaskStackView|insertIndex]",
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 9e4386f..7376255 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -93,6 +94,7 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
         setWillNotDraw(false);
+        setDim(getDim());
     }
 
     @Override
@@ -206,7 +208,7 @@
         setScaleX(1f);
         setScaleY(1f);
         setAlpha(1f);
-        mDim = 0;
+        setDim(0);
         invalidate();
     }
 
@@ -251,6 +253,8 @@
             if (isTaskViewFrontMost) {
                 // Hide the front most task bar view so we can animate it in
                 mBarView.prepareAnimateEnterRecents();
+                // Set the dim to 0 so we can animate it in
+                setDim(0);
             }
 
         } else if (mConfig.launchedFromHome) {
@@ -292,7 +296,13 @@
         } else if (mConfig.launchedFromAppWithThumbnail) {
             if (ctx.isFrontMost) {
                 // Animate the task bar of the first task view
-                mBarView.animateOnEnterRecents(-1);
+                mBarView.animateOnEnterRecents(mConfig.taskBarEnterAnimDelay);
+                // Animate the dim into view as well
+                ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
+                anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
+                anim.setDuration(mConfig.taskBarEnterAnimDuration);
+                anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+                anim.start();
             }
 
         } else if (mConfig.launchedFromHome) {
@@ -325,9 +335,27 @@
         ctx.postAnimationTrigger.increment();
     }
 
+    /** Animates this task view if the user does not interact with the stack after a certain time. */
+    public void animateOnNoUserInteraction() {
+        mBarView.animateOnNoUserInteraction();
+    }
+
+    /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+    public void setOnNoUserInteraction() {
+        mBarView.setOnNoUserInteraction();
+    }
+
     /** Animates this task view as it exits recents */
     public void animateOnLaunchingTask(final Runnable r) {
         mBarView.animateOnLaunchingTask(r);
+
+        // Animate the dim
+        if (mDim > 0) {
+            ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
+            anim.setDuration(mConfig.taskBarExitAnimDuration);
+            anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+            anim.start();
+        }
     }
 
     /** Animates the deletion of this task view */
@@ -398,13 +426,29 @@
         }
     }
 
-    /** Update the dim as a function of the scale of this view. */
-    void updateDimOverlayFromScale() {
+    /** Returns the current dim. */
+    public void setDim(int dim) {
+        mDim = dim;
+        postInvalidateOnAnimation();
+    }
+
+    /** Returns the current dim. */
+    public int getDim() {
+        return mDim;
+    }
+
+    /** Compute the dim as a function of the scale of this view. */
+    int getDimOverlayFromScale() {
         float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
         float scaleRange = 1f - minScale;
         float dim = (1f - getScaleX()) / scaleRange;
         dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
-        mDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
+        return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
+    }
+
+    /** Update the dim as a function of the scale of this view. */
+    void updateDimOverlayFromScale() {
+        setDim(getDimOverlayFromScale());
     }
 
     @Override