Creating a separate state for launcehr as background app

This makes is simpler to control the swipe to overview UI, and ties the
transition with the state machine

Bug: 113287120
Bug: 79755195
Bug: 112203163
Change-Id: I3041dbb659b46ccea6284855addc91e370815be6
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
new file mode 100644
index 0000000..53dcc74
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.launcher3.uioverrides;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.quickstep.QuickScrubController;
+import com.android.quickstep.util.LayoutUtils;
+
+/**
+ * State indicating that the Launcher is behind an app
+ */
+public class BackgroundAppState extends OverviewState {
+
+    private static final int STATE_FLAGS =
+            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+
+    public BackgroundAppState(int id) {
+        super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS);
+    }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+            return super.getVerticalProgress(launcher);
+        }
+        int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher.getDeviceProfile());
+        AllAppsTransitionController controller = launcher.getAllAppsController();
+        float scrollRange = Math.max(controller.getShiftRange(), 1);
+        float progressDelta = (transitionLength / scrollRange);
+        return super.getVerticalProgress(launcher) + progressDelta;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 25b5f57..0d77bca 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
+import android.graphics.Rect;
 import android.view.View;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -38,6 +39,8 @@
  */
 public class OverviewState extends LauncherState {
 
+    protected static final Rect sTempRect = new Rect();
+
     private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
             | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
 
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 77af211..63fc6cd 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -16,8 +16,10 @@
 package com.android.quickstep;
 
 import static android.view.View.TRANSLATION_Y;
+
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -37,7 +39,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Handler;
@@ -54,7 +55,6 @@
 import com.android.launcher3.TestProtocol;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -100,13 +100,8 @@
 
     void onTransitionCancelled(T activity, boolean activityVisible);
 
-    default int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            @InteractionType int interactionType, TransformedRect outRect) {
-        return getSwipeUpDestinationAndLength(dp, context, interactionType, outRect, null);
-    }
-
     int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            @InteractionType int interactionType, TransformedRect outRect, PointF touchTown);
+            @InteractionType int interactionType, TransformedRect outRect);
 
     void onSwipeUpComplete(T activity);
 
@@ -190,7 +185,7 @@
 
         @Override
         public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-                @InteractionType int interactionType, TransformedRect outRect, PointF touchDown) {
+                @InteractionType int interactionType, TransformedRect outRect) {
             LayoutUtils.calculateLauncherTaskSize(context, dp, outRect.rect);
             if (interactionType == INTERACTION_QUICK_SCRUB) {
                 outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context);
@@ -200,12 +195,7 @@
                 int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
                 return dp.hotseatBarSizePx + hotseatInset;
             } else {
-                int swipeLength = LayoutUtils.getShelfTrackingDistance(dp);
-                if (touchDown != null) {
-                    // We are already partway through based on where we touched the nav bar.
-                    swipeLength -= dp.heightPx - touchDown.y;
-                }
-                return swipeLength;
+                return LayoutUtils.getShelfTrackingDistance(dp);
             }
         }
 
@@ -233,24 +223,28 @@
             }
             activity.getStateManager().setRestState(resetState);
 
+            final LauncherState fromState;
             if (!activityVisible) {
                 // Since the launcher is not visible, we can safely reset the scroll position.
                 // This ensures then the next swipe up to all-apps starts from scroll 0.
                 activity.getAppsView().reset(false /* animate */);
-                activity.getStateManager().goToState(OVERVIEW, false);
+                fromState = BACKGROUND_APP;
+                activity.getStateManager().goToState(BACKGROUND_APP, false);
 
                 // Optimization, hide the all apps view to prevent layout while initializing
                 activity.getAppsView().getContentView().setVisibility(View.GONE);
 
                 AccessibilityManagerCompat.sendEventToTest(
                         activity, TestProtocol.SWITCHED_TO_STATE_MESSAGE);
+            } else {
+                fromState = startState;
             }
 
             return new AnimationFactory() {
                 @Override
                 public void createActivityController(long transitionLength,
                         @InteractionType int interactionType) {
-                    createActivityControllerInternal(activity, activityVisible, startState,
+                    createActivityControllerInternal(activity, activityVisible, fromState,
                             transitionLength, interactionType, callback);
                 }
 
@@ -262,7 +256,7 @@
         }
 
         private void createActivityControllerInternal(Launcher activity, boolean wasVisible,
-                LauncherState startState, long transitionLength,
+                LauncherState fromState, long transitionLength,
                 @InteractionType int interactionType,
                 Consumer<AnimatorPlaybackController> callback) {
             LauncherState endState = interactionType == INTERACTION_QUICK_SCRUB
@@ -271,31 +265,18 @@
                 DeviceProfile dp = activity.getDeviceProfile();
                 long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
                 callback.accept(activity.getStateManager()
-                        .createAnimationToNewWorkspace(startState, endState, accuracy));
+                        .createAnimationToNewWorkspace(fromState, endState, accuracy));
                 return;
             }
 
             AnimatorSet anim = new AnimatorSet();
-
             if (!activity.getDeviceProfile().isVerticalBarLayout()) {
                 AllAppsTransitionController controller = activity.getAllAppsController();
-                float scrollRange = Math.max(controller.getShiftRange(), 1);
-                float progressDelta = (transitionLength / scrollRange);
-
-                float endProgress = endState.getVerticalProgress(activity);
-                float startProgress = endProgress + progressDelta;
-                ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
-                        controller, ALL_APPS_PROGRESS, startProgress, endProgress);
+                ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS,
+                        fromState.getVerticalProgress(activity),
+                        endState.getVerticalProgress(activity));
                 shiftAnim.setInterpolator(LINEAR);
                 anim.play(shiftAnim);
-
-                // Since we are changing the start position of the UI, reapply the state, at the end
-                anim.addListener(new AnimationSuccessListener() {
-                    @Override
-                    public void onAnimationSuccess(Animator animator) {
-                        activity.getStateManager().reapplyState();
-                    }
-                });
             }
 
             if (interactionType == INTERACTION_NORMAL) {
@@ -304,7 +285,14 @@
 
             anim.setDuration(transitionLength * 2);
             activity.getStateManager().setCurrentAnimation(anim);
-            callback.accept(AnimatorPlaybackController.wrap(anim, transitionLength * 2));
+            AnimatorPlaybackController controller =
+                    AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+
+            // Since we are changing the start position of the UI, reapply the state, at the end
+            controller.setEndAction(() ->
+                activity.getStateManager().goToState(
+                        controller.getProgressFraction() > 0.5 ? endState : fromState, false));
+            callback.accept(controller);
         }
 
         /**
@@ -464,7 +452,7 @@
 
         @Override
         public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-                @InteractionType int interactionType, TransformedRect outRect, PointF touchDown) {
+                @InteractionType int interactionType, TransformedRect outRect) {
             LayoutUtils.calculateFallbackTaskSize(context, dp, outRect.rect);
             if (dp.isVerticalBarLayout()) {
                 Rect targetInsets = dp.getInsets();
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index bda3d06..98d723f 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -17,20 +17,23 @@
 
 import android.util.SparseArray;
 
+import com.android.launcher3.Utilities.Consumer;
+
 /**
  * Utility class to help manage multiple callbacks based on different states.
  */
 public class MultiStateCallback {
 
     private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
+    private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
 
     private int mState = 0;
 
     /**
      * Adds the provided state flags to the global state and executes any callbacks as a result.
-     * @param stateFlag
      */
     public void setState(int stateFlag) {
+        int oldState = mState;
         mState = mState | stateFlag;
 
         int count = mCallbacks.size();
@@ -46,6 +49,30 @@
                 }
             }
         }
+        notifyStateChangeHandlers(oldState);
+    }
+
+    /**
+     * Adds the provided state flags to the global state and executes any change handlers
+     * as a result.
+     */
+    public void clearState(int stateFlag) {
+        int oldState = mState;
+        mState = mState & ~stateFlag;
+        notifyStateChangeHandlers(oldState);
+    }
+
+    private void notifyStateChangeHandlers(int oldState) {
+        int count = mStateChangeHandlers.size();
+        for (int i = 0; i < count; i++) {
+            int state = mStateChangeHandlers.keyAt(i);
+            boolean wasOn = (state & oldState) == state;
+            boolean isOn = (state & mState) == state;
+
+            if (wasOn != isOn) {
+                mStateChangeHandlers.valueAt(i).accept(isOn);
+            }
+        }
     }
 
     /**
@@ -56,6 +83,13 @@
         mCallbacks.put(stateMask, callback);
     }
 
+    /**
+     * Sets the handler to be called when the provided states are enabled or disabled.
+     */
+    public void addChangeHandler(int stateMask, Consumer<Boolean> handler) {
+        mStateChangeHandlers.put(stateMask, handler);
+    }
+
     public int getState() {
         return mState;
     }
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index c3e0568..0e811f7 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -444,7 +444,7 @@
             } else {
                 TraceHelper.partitionSection("RecentsController", "Received");
                 mInteractionHandler.onRecentsAnimationStart(mController, mTargets,
-                        mHomeContentInsets, mMinimizedHomeBounds, mDownPos);
+                        mHomeContentInsets, mMinimizedHomeBounds);
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 18fbfbb..fa36f6b 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -36,7 +36,6 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
@@ -123,9 +122,11 @@
 
     private static final int STATE_CAPTURE_SCREENSHOT = 1 << 15;
     private static final int STATE_SCREENSHOT_CAPTURED = 1 << 16;
+    private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17;
 
-    private static final int STATE_RESUME_LAST_TASK = 1 << 17;
-    private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 18;
+    private static final int STATE_RESUME_LAST_TASK = 1 << 18;
+    private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 19;
+
 
     private static final int LAUNCHER_UI_STATES =
             STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
@@ -158,6 +159,7 @@
             "STATE_QUICK_SCRUB_END",
             "STATE_CAPTURE_SCREENSHOT",
             "STATE_SCREENSHOT_CAPTURED",
+            "STATE_SCREENSHOT_VIEW_SHOWN",
             "STATE_RESUME_LAST_TASK",
             "STATE_ASSIST_DATA_RECEIVED",
     };
@@ -176,13 +178,14 @@
     protected boolean mIsGoingToHome;
     private DeviceProfile mDp;
     private int mTransitionDragLength;
-    private PointF mTouchDown;
 
     // Shift in the range of [0, 1].
     // 0 => preview snapShot is completely visible, and hotseat is completely translated down
     // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
     // visible.
     private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+    // To avoid UI jump when gesture is started, we offset the animation by the threshold.
+    private float mShiftAtGestureStart = 0;
 
     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
@@ -324,6 +327,10 @@
 
         mStateCallback.addCallback(LONG_SWIPE_ENTER_STATE, this::checkLongSwipeCanEnter);
         mStateCallback.addCallback(LONG_SWIPE_START_STATE, this::checkLongSwipeCanStart);
+
+        mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
+                | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
+                (b) -> mRecentsView.setRunningTaskHidden(!b));
     }
 
     private void executeOnUiThread(Runnable action) {
@@ -342,12 +349,12 @@
         }
     }
 
-    private void initTransitionEndpoints(DeviceProfile dp, PointF touchDown) {
+    private void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
 
         TransformedRect tempRect = new TransformedRect();
         mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
-                dp, mContext, mInteractionType, tempRect, touchDown);
+                dp, mContext, mInteractionType, tempRect);
         mClipAnimationHelper.updateTargetRect(tempRect);
     }
 
@@ -571,14 +578,14 @@
      * Called by {@link #mLayoutListener} when launcher layout changes
      */
     public void buildAnimationController() {
-        initTransitionEndpoints(mActivity.getDeviceProfile(), mTouchDown);
+        initTransitionEndpoints(mActivity.getDeviceProfile());
         mAnimationFactory.createActivityController(mTransitionDragLength, mInteractionType);
     }
 
     private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
         mLauncherTransitionController = anim;
         mLauncherTransitionController.dispatchOnStart();
-        mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
+        updateLauncherTransitionProgress();
     }
 
     @WorkerThread
@@ -617,12 +624,18 @@
                 .getAnimationPlayer().isStarted()) {
             return;
         }
-        mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
+        updateLauncherTransitionProgress();
+    }
+
+    private void updateLauncherTransitionProgress() {
+        float progress = mCurrentShift.value;
+        mLauncherTransitionController.setPlayFraction(
+                progress <= mShiftAtGestureStart || mShiftAtGestureStart >= 1
+                        ? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart));
     }
 
     public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds,
-            PointF touchDown) {
+            RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
         final Rect overviewStackBounds;
         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId);
@@ -653,8 +666,7 @@
             mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
         }
         mClipAnimationHelper.prepareAnimation(false /* isOpening */);
-        mTouchDown = touchDown;
-        initTransitionEndpoints(dp, mTouchDown);
+        initTransitionEndpoints(dp);
 
         mRecentsAnimationWrapper.setController(controller, targets);
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
@@ -670,6 +682,7 @@
 
     public void onGestureStarted() {
         notifyGestureStartedAsync();
+        mShiftAtGestureStart = mCurrentShift.value;
         setStateOnUiThread(mInteractionType == INTERACTION_NORMAL
                 ? STATE_GESTURE_STARTED_QUICKSTEP : STATE_GESTURE_STARTED_QUICKSCRUB);
         mGestureStarted = true;
@@ -805,8 +818,8 @@
             @Override
             public void onAnimationSuccess(Animator animator) {
                 setStateOnUiThread(mIsGoingToHome
-                        ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT)
-                        : STATE_SCALED_CONTROLLER_APP);
+                        ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
+                        | STATE_SCREENSHOT_VIEW_SHOWN) : STATE_SCALED_CONTROLLER_APP);
             }
         });
         anim.start();
@@ -900,7 +913,6 @@
                 mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
             }
             TaskView taskView = mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot);
-            mRecentsView.setRunningTaskHidden(false);
             if (taskView != null) {
                 // Defer finishing the animation until the next launcher frame with the
                 // new thumbnail
@@ -1042,6 +1054,7 @@
 
     private void onLongSwipeDisabledUi() {
         mUiLongSwipeMode = false;
+        mStateCallback.clearState(STATE_SCREENSHOT_VIEW_SHOWN);
 
         if (mLongSwipeController != null) {
             mLongSwipeController.destroy();
@@ -1067,7 +1080,7 @@
         }
 
         // We are entering long swipe mode, make sure the screen shot is captured.
-        mStateCallback.setState(STATE_CAPTURE_SCREENSHOT);
+        mStateCallback.setState(STATE_CAPTURE_SCREENSHOT | STATE_SCREENSHOT_VIEW_SHOWN);
 
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 253e56f..6ca0dce 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -113,8 +113,7 @@
     }
 
     public static int getShelfTrackingDistance(DeviceProfile dp) {
-        int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
-        // Track slightly below the top of the shelf (between top and content).
-        return shelfHeight - dp.edgeMarginPx * 2;
+        // Start from a third of bottom inset to provide some shelf overlap.
+        return dp.hotseatBarSizePx + dp.getInsets().bottom / 3 - dp.edgeMarginPx * 2;
     }
 }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index bbe44c0..beef97e 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -22,11 +22,11 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
 
-import android.graphics.Rect;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.uioverrides.AllAppsState;
+import com.android.launcher3.uioverrides.BackgroundAppState;
 import com.android.launcher3.uioverrides.FastOverviewState;
 import com.android.launcher3.uioverrides.OverviewState;
 import com.android.launcher3.uioverrides.UiFactory;
@@ -73,7 +73,7 @@
                 }
             };
 
-    private static final LauncherState[] sAllStates = new LauncherState[5];
+    private static final LauncherState[] sAllStates = new LauncherState[6];
 
     /**
      * TODO: Create a separate class for NORMAL state.
@@ -89,8 +89,7 @@
     public static final LauncherState OVERVIEW = new OverviewState(2);
     public static final LauncherState FAST_OVERVIEW = new FastOverviewState(3);
     public static final LauncherState ALL_APPS = new AllAppsState(4);
-
-    protected static final Rect sTempRect = new Rect();
+    public static final LauncherState BACKGROUND_APP = new BackgroundAppState(5);
 
     public final int ordinal;
 
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java
new file mode 100644
index 0000000..9133b07
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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.launcher3.uioverrides;
+
+/**
+ * A dummy background app state
+ */
+public class BackgroundAppState extends OverviewState {
+
+    public BackgroundAppState(int id) {
+        super(id);
+    }
+}