Using a different transition for All-set activity

Bug: 201797003
Test: Manual
Change-Id: I33b342ca224ab283e2ac475d4defe2eac6a87bb8
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 4fbb8a0..b4ee482 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -14,81 +14,93 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingStart="@dimen/allset_page_margin_horizontal"
-    android:paddingEnd="@dimen/allset_page_margin_horizontal"
-    android:layoutDirection="locale"
-    android:textDirection="locale">
+    android:id="@+id/root_view"
+    android:background="@color/all_set_page_background" >
 
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/allset_title_icon_margin_top"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        android:src="@drawable/ic_all_set"/>
-
-    <TextView
-        android:id="@+id/title"
-        style="@style/TextAppearance.GestureTutorial.Feedback.Title"
+    <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/allset_title_margin_top"
-        app:layout_constraintTop_toBottomOf="@id/icon"
-        app:layout_constraintStart_toStartOf="parent"
-        android:gravity="start"
-        android:text="@string/allset_title"/>
+        android:layout_height="match_parent"
+        android:layout_marginStart="@dimen/allset_page_margin_horizontal"
+        android:layout_marginEnd="@dimen/allset_page_margin_horizontal"
+        android:layoutDirection="locale"
+        android:textDirection="locale"
+        android:id="@+id/content_view"
+        android:forceHasOverlappingRendering="false"
+        android:fitsSystemWindows="true" >
 
-    <TextView
-        android:id="@+id/subtitle"
-        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/allset_subtitle_margin_top"
-        app:layout_constraintTop_toBottomOf="@id/title"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintWidth_max="@dimen/allset_subtitle_width_max"
-        android:gravity="start"
-        android:text="@string/allset_description"/>
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_title_icon_margin_top"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            android:src="@drawable/ic_all_set"/>
 
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/navigation_settings_guideline_bottom"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.83" />
+        <TextView
+            android:id="@+id/title"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_title_margin_top"
+            app:layout_constraintTop_toBottomOf="@id/icon"
+            app:layout_constraintStart_toStartOf="parent"
+            android:gravity="start"
+            android:text="@string/allset_title"/>
 
-    <TextView
-        android:id="@+id/navigation_settings"
-        style="@style/TextAppearance.GestureTutorial.LinkText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
-        android:minHeight="48dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:text="@string/allset_navigation_settings" />
+        <TextView
+            android:id="@+id/subtitle"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_subtitle_margin_top"
+            app:layout_constraintTop_toBottomOf="@id/title"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintWidth_max="@dimen/allset_subtitle_width_max"
+            android:gravity="start"
+            android:text="@string/allset_description"/>
 
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/hint_guideline_bottom"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.94" />
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/navigation_settings_guideline_bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            app:layout_constraintGuide_percent="0.83" />
 
-    <TextView
-        android:id="@+id/hint"
-        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
-        android:textSize="14sp"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
-        android:text="@string/allset_hint"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
+        <TextView
+            android:id="@+id/navigation_settings"
+            style="@style/TextAppearance.GestureTutorial.LinkText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
+            android:minHeight="48dp"
+            android:background="?android:attr/selectableItemBackground"
+            android:text="@string/allset_navigation_settings" />
+
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/hint_guideline_bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            app:layout_constraintGuide_percent="0.94" />
+
+        <TextView
+            android:id="@+id/hint"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+            android:textSize="14sp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
+            android:text="@string/allset_hint"/>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/quickstep/res/values-night/colors.xml b/quickstep/res/values-night/colors.xml
index c3b2536..af6e064 100644
--- a/quickstep/res/values-night/colors.xml
+++ b/quickstep/res/values-night/colors.xml
@@ -22,4 +22,6 @@
     <color name="mock_webpage_url_bar">#202124</color>
     <color name="mock_webpage_url_bar_item">#3c4043</color>
 
+    <color name="all_set_page_background">#FF000000</color>
+
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values-night/styles.xml b/quickstep/res/values-night/styles.xml
index 1bd3f5d..e6b3450 100644
--- a/quickstep/res/values-night/styles.xml
+++ b/quickstep/res/values-night/styles.xml
@@ -21,7 +21,7 @@
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:enforceNavigationBarContrast">false</item>
         <item name="android:windowLightStatusBar">false</item>
-        <item name="android:windowBackground">#FF000000</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
     </style>
 
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 5edcc9d..1bddb57 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -72,4 +72,7 @@
     <color name="mock_webpage_top_bar">#e8eaed</color>
     <color name="mock_webpage_top_bar_item">#80868b</color>
     <color name="mock_webpage_page_text">#bdc1c6</color>
+
+    <color name="all_set_page_background">#FFFFFFFF</color>
+
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index e08eda8..fa21b0a 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -161,6 +161,7 @@
     <dimen name="allset_title_icon_margin_top">32dp</dimen>
     <dimen name="allset_subtitle_margin_top">24dp</dimen>
     <dimen name="allset_subtitle_width_max">348dp</dimen>
+    <dimen name="allset_swipe_up_shift">10dp</dimen>
 
     <!-- All Apps Education tutorial -->
     <dimen name="swipe_edu_padding">8dp</dimen>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index b5444b5..40e18ec 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -115,7 +115,7 @@
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:enforceNavigationBarContrast">false</item>
         <item name="android:windowLightStatusBar">true</item>
-        <item name="android:windowBackground">#FFFFFFFF</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
     </style>
 
     <!--
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 3580ee5..0b09323 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -36,7 +36,7 @@
     int TYPE_SCREEN_PINNED = 1 << 6;
     int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
     int TYPE_RESET_GESTURE = 1 << 8;
-    int TYPE_OVERSCROLL = 1 << 9;
+    int TYPE_PROGRESS_DELEGATE = 1 << 9;
     int TYPE_SYSUI_OVERLAY = 1 << 10;
     int TYPE_ONE_HANDED = 1 << 11;
     int TYPE_TASKBAR_STASH = 1 << 12;
@@ -51,7 +51,7 @@
             "TYPE_SCREEN_PINNED",           // 6
             "TYPE_OVERVIEW_WITHOUT_FOCUS",  // 7
             "TYPE_RESET_GESTURE",           // 8
-            "TYPE_OVERSCROLL",              // 9
+            "TYPE_PROGRESS_DELEGATE",       // 9
             "TYPE_SYSUI_OVERLAY",           // 10
             "TYPE_ONE_HANDED",              // 11
             "TYPE_TASKBAR_STASH",           // 12
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ddcf34c..368589e 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -90,6 +90,7 @@
 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
+import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer;
 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
 import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
 import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
@@ -119,6 +120,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.LinkedList;
+import java.util.function.Function;
 
 /**
  * Service connected by system-UI for handling touch interaction.
@@ -298,6 +300,13 @@
         public OverviewCommandHelper getOverviewCommandHelper() {
             return mOverviewCommandHelper;
         }
+
+        /**
+         * Sets a proxy to bypass swipe up behavior
+         */
+        public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) {
+            mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null);
+        }
     }
 
     private static boolean sConnected = false;
@@ -336,6 +345,7 @@
     private DisplayManager mDisplayManager;
 
     private TaskbarManager mTaskbarManager;
+    private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
 
     @Override
     public void onCreate() {
@@ -647,6 +657,12 @@
 
     private InputConsumer newConsumer(GestureState previousGestureState,
             GestureState newGestureState, MotionEvent event) {
+        AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
+        if (progressProxy != null) {
+            return new ProgressDelegateInputConsumer(this, mTaskAnimationManager,
+                    mGestureState, mInputMonitorCompat, progressProxy);
+        }
+
         boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
 
         if (!mDeviceState.isUserUnlocked()) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
new file mode 100644
index 0000000..c69b510
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2021 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.quickstep.inputconsumers;
+
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.touch.BaseSwipeDetector.calculateDuration;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.view.MotionEvent;
+
+import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.MultiStateCallback;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.TaskAnimationManager;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Input consumer which delegates the swipe-progress handling
+ */
+public class ProgressDelegateInputConsumer implements InputConsumer,
+        RecentsAnimationCallbacks.RecentsAnimationListener,
+        SingleAxisSwipeDetector.Listener {
+
+    private static final float SWIPE_DISTANCE_THRESHOLD = 0.2f;
+
+    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[3] : null;
+    private static int getFlagForIndex(int index, String name) {
+        if (DEBUG_STATES) {
+            STATE_NAMES[index] = name;
+        }
+        return 1 << index;
+    }
+
+    private static final int STATE_TARGET_RECEIVED =
+            getFlagForIndex(0, "STATE_TARGET_RECEIVED");
+    private static final int STATE_HANDLER_INVALIDATED =
+            getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
+    private static final int STATE_FLING_FINISHED =
+            getFlagForIndex(2, "STATE_FLING_FINISHED");
+
+    private final Context mContext;
+    private final TaskAnimationManager mTaskAnimationManager;
+    private final GestureState mGestureState;
+    private final InputMonitorCompat mInputMonitorCompat;
+    private final MultiStateCallback mStateCallback;
+
+    private final Point mDisplaySize;
+    private final SingleAxisSwipeDetector mSwipeDetector;
+
+    private final AnimatedFloat mProgress;
+
+    private boolean mDragStarted = false;
+
+    private RecentsAnimationController mRecentsAnimationController;
+    private Boolean mFlingEndsOnHome;
+
+    public ProgressDelegateInputConsumer(Context context,
+            TaskAnimationManager taskAnimationManager, GestureState gestureState,
+            InputMonitorCompat inputMonitorCompat, AnimatedFloat progress) {
+        mContext = context;
+        mTaskAnimationManager = taskAnimationManager;
+        mGestureState = gestureState;
+        mInputMonitorCompat = inputMonitorCompat;
+        mProgress = progress;
+
+        // Do not use DeviceProfile as the user data might be locked
+        mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
+
+        // Init states
+        mStateCallback = new MultiStateCallback(STATE_NAMES);
+        mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
+                this::endRemoteAnimation);
+        mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
+                this::onFlingFinished);
+
+        mSwipeDetector = new SingleAxisSwipeDetector(mContext, this, VERTICAL);
+        mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_PROGRESS_DELEGATE;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mFlingEndsOnHome == null) {
+            mSwipeDetector.onTouchEvent(ev);
+        }
+    }
+
+    @Override
+    public void onDragStart(boolean start, float startDisplacement) {
+        mDragStarted = true;
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
+        mInputMonitorCompat.pilferPointers();
+        Intent intent = mGestureState.getHomeIntent()
+                .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
+        mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
+    }
+
+    @Override
+    public boolean onDrag(float displacement) {
+        if (mDisplaySize.y > 0) {
+            mProgress.updateValue(displacement / -mDisplaySize.y);
+        }
+        return true;
+    }
+
+    @Override
+    public void onDragEnd(float velocity) {
+        final boolean willExit;
+        if (mSwipeDetector.isFling(velocity)) {
+            willExit = velocity < 0;
+        } else {
+            willExit = mProgress.value > SWIPE_DISTANCE_THRESHOLD;
+        }
+        float endValue = willExit ? 1 : 0;
+        long duration = calculateDuration(velocity, endValue - mProgress.value);
+        mFlingEndsOnHome = willExit;
+
+        ObjectAnimator anim = mProgress.animateToValue(endValue);
+        anim.setDuration(duration).setInterpolator(scrollInterpolatorForVelocity(velocity));
+        if (mRecentsAnimationController != null) {
+            anim.addListener(AnimatorListeners.forSuccessCallback(
+                    () -> mStateCallback.setState(STATE_FLING_FINISHED)));
+        }
+        anim.start();
+    }
+
+    private void onFlingFinished() {
+        if (mRecentsAnimationController != null) {
+            boolean endToRecents = mFlingEndsOnHome == null ? true : mFlingEndsOnHome;
+            mRecentsAnimationController.finishController(endToRecents /* toRecents */,
+                    null /* callback */, false /* sendUserLeaveHint */);
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationStart(RecentsAnimationController controller,
+            RecentsAnimationTargets targets) {
+        mRecentsAnimationController = controller;
+        mStateCallback.setState(STATE_TARGET_RECEIVED);
+    }
+
+    @Override
+    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+        mRecentsAnimationController = null;
+    }
+
+    private void endRemoteAnimation() {
+        onDragEnd(Float.MIN_VALUE);
+    }
+
+    @Override
+    public void onConsumerAboutToBeSwitched() {
+        mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return !mDragStarted;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index f731cb3..272a9a1 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -15,10 +15,28 @@
  */
 package com.android.quickstep.interaction;
 
+import static com.android.launcher3.Utilities.mapBoundToRange;
+import static com.android.launcher3.Utilities.mapRange;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
 import android.app.Activity;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PointF;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
@@ -29,9 +47,12 @@
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.GestureState;
 import com.android.quickstep.TouchInteractionService.TISBinder;
 import com.android.quickstep.util.TISBindHelper;
 
@@ -49,13 +70,23 @@
     private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
     private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
 
+    private static final float HINT_BOTTOM_FACTOR = 1 - .94f;
+
     private TISBindHelper mTISBindHelper;
     private TISBinder mBinder;
 
+    private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate);
+    private BgDrawable mBackground;
+    private View mContentView;
+    private float mSwipeUpShift;
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_allset);
+        findViewById(R.id.root_view).setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
 
         int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
         boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
@@ -65,6 +96,11 @@
 
         ((ImageView) findViewById(R.id.icon)).getDrawable().mutate().setTint(accentColor);
 
+        mBackground = new BgDrawable(this);
+        findViewById(R.id.root_view).setBackground(mBackground);
+        mContentView = findViewById(R.id.content_view);
+        mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
+
         TextView tv = findViewById(R.id.navigation_settings);
         tv.setTextColor(accentColor);
         tv.setOnClickListener(v -> {
@@ -86,19 +122,26 @@
         super.onResume();
         if (mBinder != null) {
             mBinder.getTaskbarManager().setSetupUIVisible(true);
+            mBinder.setSwipeUpProxy(this::createSwipeUpProxy);
         }
     }
 
     private void onTISConnected(TISBinder binder) {
         mBinder = binder;
         mBinder.getTaskbarManager().setSetupUIVisible(isResumed());
+        mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
     }
 
     @Override
     protected void onPause() {
         super.onPause();
+        clearBinderOverride();
+    }
+
+    private void clearBinderOverride() {
         if (mBinder != null) {
             mBinder.getTaskbarManager().setSetupUIVisible(false);
+            mBinder.setSwipeUpProxy(null);
         }
     }
 
@@ -106,6 +149,27 @@
     protected void onDestroy() {
         super.onDestroy();
         mTISBindHelper.onDestroy();
+        clearBinderOverride();
+    }
+
+    private AnimatedFloat createSwipeUpProxy(GestureState state) {
+        if (!state.getHomeIntent().getComponent().getPackageName().equals(getPackageName())) {
+            return null;
+        }
+        RunningTaskInfo rti = state.getRunningTask();
+        if (rti == null || !rti.topActivity.equals(getComponentName())) {
+            return null;
+        }
+        mSwipeProgress.updateValue(0);
+        return mSwipeProgress;
+    }
+
+    private void onSwipeProgressUpdate() {
+        mBackground.setProgress(mSwipeProgress.value);
+        float alpha = Utilities.mapBoundToRange(mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR,
+                1, 0, LINEAR);
+        mContentView.setAlpha(alpha);
+        mContentView.setTranslationY((alpha - 1) * mSwipeUpShift);
     }
 
     /**
@@ -132,4 +196,79 @@
             return super.performAccessibilityAction(host, action, args);
         }
     }
+
+    private static class BgDrawable extends Drawable {
+
+        private static final float START_SIZE_FACTOR = .5f;
+        private static final float END_SIZE_FACTOR = 2;
+        private static final float GRADIENT_END_PROGRESS = .5f;
+
+        private final Paint mPaint = new Paint();
+        private final RadialGradient mMaskGrad;
+        private final Matrix mMatrix = new Matrix();
+
+        private final ColorMatrix mColorMatrix = new ColorMatrix();
+        private final ColorMatrixColorFilter mColorFilter =
+                new ColorMatrixColorFilter(mColorMatrix);
+
+        private final int mColor;
+        private float mProgress = 0;
+
+        BgDrawable(Context context) {
+            mColor = context.getColor(R.color.all_set_page_background);
+            mMaskGrad = new RadialGradient(0, 0, 1,
+                    new int[] {ColorUtils.setAlphaComponent(mColor, 0), mColor},
+                    new float[]{0, 1}, TileMode.CLAMP);
+
+            mPaint.setShader(mMaskGrad);
+            mPaint.setColorFilter(mColorFilter);
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            if (mProgress <= 0) {
+                canvas.drawColor(mColor);
+                return;
+            }
+
+            // Update the progress to half the size only.
+            float progress = mapBoundToRange(mProgress,
+                    0, GRADIENT_END_PROGRESS, 0, 1, LINEAR);
+            Rect bounds = getBounds();
+            float x = bounds.exactCenterX();
+            float height = bounds.height();
+
+            float size = PointF.length(x, height);
+            float radius = size * mapRange(progress, START_SIZE_FACTOR, END_SIZE_FACTOR);
+            float y = mapRange(progress, height + radius , height / 2);
+            mMatrix.setTranslate(x, y);
+            mMatrix.postScale(radius, radius, x, y);
+            mMaskGrad.setLocalMatrix(mMatrix);
+
+            // Change the alpha-addition-component (index 19) so that every pixel is updated
+            // accordingly
+            mColorMatrix.getArray()[19] = mapBoundToRange(mProgress, 0, 1, 0, -255, LINEAR);
+            mColorFilter.setColorMatrix(mColorMatrix);
+
+            canvas.drawPaint(mPaint);
+        }
+
+        public void setProgress(float progress) {
+            if (mProgress != progress) {
+                mProgress = progress;
+                invalidateSelf();
+            }
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+
+        @Override
+        public void setAlpha(int i) { }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) { }
+    }
 }