Subclassing Launcher instead of using UiFactory

Allows us to override only the required methods, instead of providing
a proxy method for everything

Change-Id: I816dcdb2a8d5432496050118ded0f2bbe7122cf7
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
new file mode 100644
index 0000000..60afddb
--- /dev/null
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2019, 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.
+*/
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.launcher3">
+    <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+    <!--
+    Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
+    Refer comments around specific entries on how to extend individual components.
+    -->
+
+    <application
+        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+        android:fullBackupOnly="true"
+        android:fullBackupContent="@xml/backupscheme"
+        android:hardwareAccelerated="true"
+        android:icon="@drawable/ic_launcher_home"
+        android:label="@string/derived_app_name"
+        android:theme="@style/AppTheme"
+        android:largeHeap="@bool/config_largeHeap"
+        android:restoreAnyVersion="true"
+        android:supportsRtl="true" >
+
+        <!--
+        Main launcher activity. When extending only change the name, and keep all the
+        attributes and intent filters the same
+        -->
+        <activity
+            android:name="com.android.launcher3.uioverrides.QuickstepLauncher"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:stateNotNeeded="true"
+            android:windowSoftInputMode="adjustPan"
+            android:screenOrientation="unspecified"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+            android:resizeableActivity="true"
+            android:resumeWhilePausing="true"
+            android:taskAffinity=""
+            android:enabled="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.MONKEY"/>
+                <category android:name="android.intent.category.LAUNCHER_APP" />
+            </intent-filter>
+            <meta-data
+                android:name="com.android.launcher3.grid.control"
+                android:value="${packageName}.grid_control" />
+        </activity>
+
+    </application>
+</manifest>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
similarity index 76%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 2a22e9d..43cdbdb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -21,13 +20,14 @@
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.view.Gravity;
 
+import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
@@ -49,21 +49,15 @@
 
 import java.util.ArrayList;
 
-/**
- * Provides recents-related {@link UiFactory} logic and classes.
- */
-public abstract class RecentsUiFactory {
-
-    private static final String TAG = RecentsUiFactory.class.getSimpleName();
+public class QuickstepLauncher extends BaseQuickstepLauncher {
 
     public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
 
     /**
      * Reusable command for applying the shelf height on the background thread.
      */
-    public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) -> {
-        SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
-    };
+    public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
+            SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
 
     public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
         @Override
@@ -138,71 +132,78 @@
         }
     };
 
-    public static RotationMode getRotationMode(DeviceProfile dp) {
+    @Override
+    protected RotationMode getFakeRotationMode(DeviceProfile dp) {
         return !dp.isVerticalBarLayout() ? RotationMode.NORMAL
                 : (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE);
     }
 
-    public static TouchController[] createTouchControllers(Launcher launcher) {
-        Mode mode = SysUINavigationMode.getMode(launcher);
-
-        ArrayList<TouchController> list = new ArrayList<>();
-        list.add(launcher.getDragController());
-        if (mode == NO_BUTTON) {
-            list.add(new QuickSwitchTouchController(launcher));
-            list.add(new NavBarToHomeTouchController(launcher));
-            list.add(new FlingAndHoldTouchController(launcher));
-        } else {
-            if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-                list.add(new OverviewToAllAppsTouchController(launcher));
-                list.add(new LandscapeEdgeSwipeController(launcher));
-                if (mode.hasGestures) {
-                    list.add(new TransposedQuickSwitchTouchController(launcher));
-                }
-            } else {
-                list.add(new PortraitStatesTouchController(launcher,
-                        mode.hasGestures /* allowDragToOverview */));
-                if (mode.hasGestures) {
-                    list.add(new QuickSwitchTouchController(launcher));
-                }
-            }
-        }
-
-        if (!launcher.getDeviceProfile().isMultiWindowMode) {
-            list.add(new StatusBarTouchController(launcher));
-        }
-
-        list.add(new LauncherTaskViewController(launcher));
-        return list.toArray(new TouchController[list.size()]);
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        onStateOrResumeChanged();
     }
 
-    /**
-     * Creates and returns the controller responsible for recents view state transitions.
-     *
-     * @param launcher the launcher activity
-     * @return state handler for recents
-     */
-    public static StateHandler createRecentsViewStateController(Launcher launcher) {
-        return new RecentsViewStateController(launcher);
+    @Override
+    protected void onActivityFlagsChanged(int changeBits) {
+        super.onActivityFlagsChanged(changeBits);
+
+        if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
+                | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0
+                && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
+            onStateOrResumeChanged();
+        }
     }
 
     /**
      * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
-     *
-     * @param launcher the launcher activity
      */
-    public static void onLauncherStateOrResumeChanged(Launcher launcher) {
-        LauncherState state = launcher.getStateManager().getState();
-        DeviceProfile profile = launcher.getDeviceProfile();
-        boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+    private void onStateOrResumeChanged() {
+        LauncherState state = getStateManager().getState();
+        DeviceProfile profile = getDeviceProfile();
+        boolean visible = (state == NORMAL || state == OVERVIEW) && isUserActive()
                 && !profile.isVerticalBarLayout();
-        UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT, visible ? 1 : 0,
+        UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
                 profile.hotseatBarSizePx);
         if (state == NORMAL) {
-            launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
+            ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
         }
     }
 
+    @Override
+    public TouchController[] createTouchControllers() {
+        Mode mode = SysUINavigationMode.getMode(this);
+
+        ArrayList<TouchController> list = new ArrayList<>();
+        list.add(getDragController());
+        if (mode == NO_BUTTON) {
+            list.add(new QuickSwitchTouchController(this));
+            list.add(new NavBarToHomeTouchController(this));
+            list.add(new FlingAndHoldTouchController(this));
+        } else {
+            if (getDeviceProfile().isVerticalBarLayout()) {
+                list.add(new OverviewToAllAppsTouchController(this));
+                list.add(new LandscapeEdgeSwipeController(this));
+                if (mode.hasGestures) {
+                    list.add(new TransposedQuickSwitchTouchController(this));
+                }
+            } else {
+                list.add(new PortraitStatesTouchController(this,
+                        mode.hasGestures /* allowDragToOverview */));
+                if (mode.hasGestures) {
+                    list.add(new QuickSwitchTouchController(this));
+                }
+            }
+        }
+
+        if (!getDeviceProfile().isMultiWindowMode) {
+            list.add(new StatusBarTouchController(this));
+        }
+
+        list.add(new LauncherTaskViewController(this));
+        return list.toArray(new TouchController[list.size()]);
+    }
+
     private static final class LauncherTaskViewController extends
             TaskViewTouchController<Launcher> {
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
index bbb318a..e2524b1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
@@ -15,8 +15,8 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_LANDSCAPE;
-import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_SEASCAPE;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.ROTATION_LANDSCAPE;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.ROTATION_SEASCAPE;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 
 import android.content.Context;
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
new file mode 100644
index 0000000..f55ef94
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.proxy.ProxyActivityStarter;
+import com.android.launcher3.proxy.StartActivityParams;
+import com.android.launcher3.uioverrides.BackButtonAlphaHandler;
+import com.android.launcher3.uioverrides.RecentsViewStateController;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.RemoteFadeOutAnimationListener;
+
+/**
+ * Extension of Launcher activity to provide quickstep specific functionality
+ */
+public abstract class BaseQuickstepLauncher extends Launcher
+        implements NavigationModeChangeListener {
+
+    /**
+     * Reusable command for applying the back button alpha on the background thread.
+     */
+    public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
+            (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(
+                    Float.intBitsToFloat(arg1), arg2 != 0);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this)
+                .addModeChangeListener(this);
+        getRotationHelper().setRotationHadDifferentUI(mode != Mode.NO_BUTTON);
+
+        if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+            getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+                @Override
+                public void onStateTransitionStart(LauncherState toState) { }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
+                            .get(BaseQuickstepLauncher.this).getMode().hasGestures;
+                    LauncherState prevState = getStateManager().getLastState();
+
+                    if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
+                            && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT
+                            <= getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
+                        getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+                        getStateManager().removeStateListener(this);
+                    }
+                }
+            });
+        }
+
+        if (!getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
+            getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+                @Override
+                public void onStateTransitionStart(LauncherState toState) { }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    LauncherState prevState = getStateManager().getLastState();
+
+                    if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT
+                            <= getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
+                        getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
+                        getStateManager().removeStateListener(this);
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onNavigationModeChanged(Mode newMode) {
+        getDragLayer().recreateControllers();
+        getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON);
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+        // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+        // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+        // overview
+        RecentsModel.INSTANCE.get(this).getThumbnailCache()
+                .getHighResLoadingState().setVisible(true);
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        super.onTrimMemory(level);
+        RecentsModel.INSTANCE.get(this).onTrimMemory(level);
+    }
+
+    @Override
+    public void startIntentSenderForResult(IntentSender intent, int requestCode,
+            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
+        if (requestCode != -1) {
+            mPendingActivityRequestCode = requestCode;
+            StartActivityParams params = new StartActivityParams(this, requestCode);
+            params.intentSender = intent;
+            params.fillInIntent = fillInIntent;
+            params.flagsMask = flagsMask;
+            params.flagsValues = flagsValues;
+            params.extraFlags = extraFlags;
+            params.options = options;
+            startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+        } else {
+            super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
+                    flagsValues, extraFlags, options);
+        }
+    }
+
+    @Override
+    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
+        if (requestCode != -1) {
+            mPendingActivityRequestCode = -1;
+            StartActivityParams params = new StartActivityParams(this, requestCode);
+            params.intent = intent;
+            params.options = options;
+            startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
+        } else {
+            super.startActivityForResult(intent, requestCode, options);
+        }
+    }
+
+    @Override
+    protected void onDeferredResumed() {
+        if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+            // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
+            onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
+            // ProxyActivityStarter is started with clear task to reset the task after which it
+            // removes the task itself.
+            startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
+        }
+    }
+
+    @Override
+    protected StateHandler[] createStateHandlers() {
+        return new StateHandler[] {
+                getAllAppsController(),
+                getWorkspace(),
+                new RecentsViewStateController(this),
+                new BackButtonAlphaHandler(this)};
+    }
+
+    @Override
+    protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
+        if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
+            float offscreenTranslationX = getDeviceProfile().widthPx
+                    - getOverviewPanel().getPaddingStart();
+            return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
+        }
+        return super.getOverviewScaleAndTranslationForNormalState();
+    }
+
+    @Override
+    public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
+        QuickstepAppTransitionManagerImpl appTransitionManager =
+                (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
+        appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+
+            // On the first call clear the reference.
+            signal.cancel();
+
+            ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+            fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+                    wallpaperTargets));
+            AnimatorSet anim = new AnimatorSet();
+            anim.play(fadeAnimation);
+            return anim;
+        }, signal);
+    }
+
+    @Override
+    public void onDragLayerHierarchyChanged() {
+        onLauncherStateOrFocusChanged();
+    }
+
+    @Override
+    protected void onActivityFlagsChanged(int changeBits) {
+        if ((changeBits
+                & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
+            onLauncherStateOrFocusChanged();
+        }
+
+        super.onActivityFlagsChanged(changeBits);
+    }
+
+    /**
+     * Sets the back button visibility based on the current state/window focus.
+     */
+    private void onLauncherStateOrFocusChanged() {
+        Mode mode = SysUINavigationMode.getMode(this);
+        boolean shouldBackButtonBeHidden = mode.hasGestures
+                && getStateManager().getState().hideBackButton
+                && hasWindowFocus()
+                && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
+        if (shouldBackButtonBeHidden) {
+            // Show the back button if there is a floating view visible.
+            shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
+                    TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
+        }
+        UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
+                shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
+        if (getDragLayer() != null) {
+            getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
new file mode 100644
index 0000000..965b5f0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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 android.app.Activity;
+import android.app.Person;
+import android.content.pm.ShortcutInfo;
+import android.util.Base64;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.shared.system.ActivityCompat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.zip.Deflater;
+
+public class ApiWrapper {
+
+    public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+        if (!Utilities.IS_DEBUG_DEVICE) {
+            return false;
+        }
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
+            return false;
+        }
+
+        Deflater deflater = new Deflater();
+        deflater.setInput(out.toByteArray());
+        deflater.finish();
+
+        out.reset();
+        byte[] buffer = new byte[1024];
+        while (!deflater.finished()) {
+            int count = deflater.deflate(buffer); // returns the generated code... index
+            out.write(buffer, 0, count);
+        }
+
+        writer.println("--encoded-view-dump-v0--");
+        writer.println(Base64.encodeToString(
+                out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
+        return true;
+    }
+
+    public static Person[] getPersons(ShortcutInfo si) {
+        Person[] persons = si.getPersons();
+        return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
index aa0dfc3..43dc882 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
@@ -16,11 +16,9 @@
 
 package com.android.launcher3.uioverrides;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.anim.AnimatorSetBuilder;
@@ -30,18 +28,14 @@
 
 public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
 
-    private static final String TAG = "BackButtonAlphaHandler";
+    private final BaseQuickstepLauncher mLauncher;
 
-    private final Launcher mLauncher;
-
-    public BackButtonAlphaHandler(Launcher launcher) {
+    public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) {
         mLauncher = launcher;
     }
 
     @Override
-    public void setState(LauncherState state) {
-        UiFactory.onLauncherStateOrFocusChanged(mLauncher);
-    }
+    public void setState(LauncherState state) { }
 
     @Override
     public void setStateWithAnimation(LauncherState toState,
@@ -52,8 +46,8 @@
 
         if (!SysUINavigationMode.getMode(mLauncher).hasGestures) {
             // If the nav mode is not gestural, then force back button alpha to be 1
-            UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA, 1f,
-                    true /* animate */);
+            UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
+                    BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, 1f, true /* animate */);
             return;
         }
 
@@ -64,15 +58,8 @@
             anim.setDuration(config.duration);
             anim.addUpdateListener(valueAnimator -> {
                 final float alpha = (float) valueAnimator.getAnimatedValue();
-                UiThreadHelper.setBackButtonAlphaAsync(mLauncher, UiFactory.SET_BACK_BUTTON_ALPHA,
-                        alpha, false /* animate */);
-            });
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    // Reapply the final alpha in case some state (e.g. window focus) changed.
-                    UiFactory.onLauncherStateOrFocusChanged(mLauncher);
-                }
+                UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
+                        BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, alpha, false /* animate */);
             });
             builder.play(anim);
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
deleted file mode 100644
index 17c681b..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2017 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 static android.app.Activity.RESULT_CANCELED;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
-
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.app.Activity;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.util.Base64;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.QuickstepAppTransitionManagerImpl;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.proxy.ProxyActivityStarter;
-import com.android.launcher3.proxy.StartActivityParams;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.RemoteFadeOutAnimationListener;
-import com.android.systemui.shared.system.ActivityCompat;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-import java.util.zip.Deflater;
-
-public class UiFactory extends RecentsUiFactory {
-
-    /**
-     * Reusable command for applying the back button alpha on the background thread.
-     */
-    public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
-            (context, arg1, arg2) -> {
-        SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(Float.intBitsToFloat(arg1),
-                arg2 != 0);
-    };
-
-    public static Runnable enableLiveUIChanges(Launcher launcher) {
-        NavigationModeChangeListener listener = m -> {
-            launcher.getDragLayer().recreateControllers();
-            launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
-        };
-        SysUINavigationMode mode = SysUINavigationMode.INSTANCE.get(launcher);
-        SysUINavigationMode.Mode m = mode.addModeChangeListener(listener);
-        launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
-        return () -> mode.removeModeChangeListener(listener);
-    }
-
-    public static StateHandler[] getStateHandler(Launcher launcher) {
-        return new StateHandler[] {
-                launcher.getAllAppsController(),
-                launcher.getWorkspace(),
-                createRecentsViewStateController(launcher),
-                new BackButtonAlphaHandler(launcher)};
-    }
-
-    /**
-     * Sets the back button visibility based on the current state/window focus.
-     */
-    public static void onLauncherStateOrFocusChanged(Launcher launcher) {
-        Mode mode = SysUINavigationMode.getMode(launcher);
-        boolean shouldBackButtonBeHidden = mode.hasGestures
-                && launcher != null
-                && launcher.getStateManager().getState().hideBackButton
-                && launcher.hasWindowFocus();
-        if (shouldBackButtonBeHidden) {
-            // Show the back button if there is a floating view visible.
-            shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
-                    TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
-        }
-        UiThreadHelper.setBackButtonAlphaAsync(launcher, UiFactory.SET_BACK_BUTTON_ALPHA,
-                shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
-        if (launcher != null && launcher.getDragLayer() != null) {
-            launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
-        }
-    }
-
-    public static void onCreate(Launcher launcher) {
-        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
-            launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
-                @Override
-                public void onStateTransitionStart(LauncherState toState) {
-                }
-
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    boolean swipeUpEnabled = SysUINavigationMode.INSTANCE.get(launcher).getMode()
-                            .hasGestures;
-                    LauncherState prevState = launcher.getStateManager().getLastState();
-
-                    if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
-                            && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT <=
-                            launcher.getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
-                        launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
-                        launcher.getStateManager().removeStateListener(this);
-                    }
-                }
-            });
-        }
-
-        if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
-            launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
-                @Override
-                public void onStateTransitionStart(LauncherState toState) {
-                }
-
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    LauncherState prevState = launcher.getStateManager().getLastState();
-
-                    if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT <=
-                            launcher.getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
-                        launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
-                        launcher.getStateManager().removeStateListener(this);
-                    }
-                }
-            });
-        }
-    }
-
-    public static void onEnterAnimationComplete(Context context) {
-        // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
-        // as a part of quickstep, so that high-res thumbnails can load the next time we enter
-        // overview
-        RecentsModel.INSTANCE.get(context).getThumbnailCache()
-                .getHighResLoadingState().setVisible(true);
-    }
-
-    public static void onTrimMemory(Context context, int level) {
-        RecentsModel model = RecentsModel.INSTANCE.get(context);
-        if (model != null) {
-            model.onTrimMemory(level);
-        }
-    }
-
-    public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
-            CancellationSignal cancellationSignal) {
-        QuickstepAppTransitionManagerImpl appTransitionManager =
-                (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
-        appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
-
-            // On the first call clear the reference.
-            cancellationSignal.cancel();
-
-            ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
-            fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
-                    wallpaperTargets));
-            AnimatorSet anim = new AnimatorSet();
-            anim.play(fadeAnimation);
-            return anim;
-        }, cancellationSignal);
-    }
-
-    public static boolean dumpActivity(Activity activity, PrintWriter writer) {
-        if (!Utilities.IS_DEBUG_DEVICE) {
-            return false;
-        }
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
-            return false;
-        }
-
-        Deflater deflater = new Deflater();
-        deflater.setInput(out.toByteArray());
-        deflater.finish();
-
-        out.reset();
-        byte[] buffer = new byte[1024];
-        while (!deflater.finished()) {
-            int count = deflater.deflate(buffer); // returns the generated code... index
-            out.write(buffer, 0, count);
-        }
-
-        writer.println("--encoded-view-dump-v0--");
-        writer.println(Base64.encodeToString(
-                out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
-        return true;
-    }
-
-    public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
-            int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
-            Bundle options) {
-        StartActivityParams params = new StartActivityParams(activity, requestCode);
-        params.intentSender = intent;
-        params.fillInIntent = fillInIntent;
-        params.flagsMask = flagsMask;
-        params.flagsValues = flagsValues;
-        params.extraFlags = extraFlags;
-        params.options = options;
-        ((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
-        return true;
-    }
-
-    public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
-            Bundle options) {
-        StartActivityParams params = new StartActivityParams(activity, requestCode);
-        params.intent = intent;
-        params.options = options;
-        activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
-        return true;
-    }
-
-    /**
-     * Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher.
-     *
-     * ProxyActivityStarter is started with clear task to reset the task after which it removes the
-     * task itself.
-     */
-    public static void resetPendingActivityResults(Launcher launcher, int requestCode) {
-        launcher.onActivityResult(requestCode, RESULT_CANCELED, null);
-        launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null));
-    }
-
-    public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
-        if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) {
-            float offscreenTranslationX = l.getDeviceProfile().widthPx
-                    - l.getOverviewPanel().getPaddingStart();
-            return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
-        }
-        return new ScaleAndTranslation(1.1f, 0f, 0f);
-    }
-
-    public static Person[] getPersons(ShortcutInfo si) {
-        Person[] persons = si.getPersons();
-        return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
index 71833ad..5fcdc19 100644
--- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -27,7 +27,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
@@ -122,13 +121,17 @@
     @Override
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
-        UiFactory.onEnterAnimationComplete(this);
+        // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
+        // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+        // overview
+        RecentsModel.INSTANCE.get(this).getThumbnailCache()
+                .getHighResLoadingState().setVisible(true);
     }
 
     @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
-        UiFactory.onTrimMemory(this, level);
+        RecentsModel.INSTANCE.get(this).onTrimMemory(level);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 289a129..4f7d53b 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -15,7 +15,7 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
+import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.content.ComponentName;