Initial commit of 3 button work

Home, Back and Recents basic functionality working

Fixes: 180046394
Change-Id: Ifc5c767e35e88183500d14d14736eb40df436369
diff --git a/quickstep/res/drawable/ic_sysbar_back.xml b/quickstep/res/drawable/ic_sysbar_back.xml
new file mode 100644
index 0000000..1eea677
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_back.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:autoMirrored="true"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M6.49,14.86c-0.66-0.39-0.66-1.34,0-1.73l6.02-3.53l5.89-3.46C19.11,5.73,20,6.26,20,7.1V14v6.9 c0,0.84-0.89,1.37-1.6,0.95l-5.89-3.46L6.49,14.86z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_home.xml b/quickstep/res/drawable/ic_sysbar_home.xml
new file mode 100644
index 0000000..b4b397b
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_home.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 14 7 C 17.8659932488 7 21 10.1340067512 21 14 C 21 17.8659932488 17.8659932488 21 14 21 C 10.1340067512 21 7 17.8659932488 7 14 C 7 10.1340067512 10.1340067512 7 14 7 Z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_recent.xml b/quickstep/res/drawable/ic_sysbar_recent.xml
new file mode 100644
index 0000000..f8c4778
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_recent.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.9,21.5H8.1c-0.88,0-1.6-0.72-1.6-1.6V8.1c0-0.88,0.72-1.6,1.6-1.6h11.8c0.88,0,1.6,0.72,1.6,1.6v11.8 C21.5,20.78,20.78,21.5,19.9,21.5z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
new file mode 100644
index 0000000..d6160de
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 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.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/taskbar_icon_selection_ripple">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+            <corners android:radius="8dp" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 3bc8ddc..167c7c3 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -27,4 +27,5 @@
 
     <!-- Taskbar -->
     <color name="taskbar_background">#101010</color>
+    <color name="taskbar_icon_selection_ripple">#E0E0E0</color>
 </resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index c93de00..cf6cfb3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -48,9 +48,12 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.TaskAnimationManager;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -73,9 +76,13 @@
     private final TaskbarAnimationController mTaskbarAnimationController;
     private final TaskbarHotseatController mHotseatController;
     private final TaskbarDragController mDragController;
+    private final TaskbarNavButtonController mNavButtonController;
 
     // Initialized in init().
     private WindowManager.LayoutParams mWindowLayoutParams;
+    private SysUINavigationMode.Mode mNavMode = SysUINavigationMode.Mode.NO_BUTTON;
+    private final SysUINavigationMode.NavigationModeChangeListener mNavigationModeChangeListener =
+            this::onNavModeChanged;
 
     private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
@@ -89,6 +96,7 @@
         mTaskbarViewInApp.construct(createTaskbarViewCallbacks());
         mTaskbarViewOnHome = taskbarViewOnHome;
         mTaskbarViewOnHome.construct(createTaskbarViewCallbacks());
+        mNavButtonController = new TaskbarNavButtonController(launcher);
         mWindowManager = mLauncher.getWindowManager();
         mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
         mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
@@ -212,6 +220,11 @@
                     alignRealHotseatWithTaskbar();
                 }
             }
+
+            @Override
+            public void onNavigationButtonClick(@TaskbarButton int buttonType) {
+                mNavButtonController.onButtonClick(buttonType);
+            }
         };
     }
 
@@ -228,8 +241,10 @@
      * Initializes the Taskbar, including adding it to the screen.
      */
     public void init() {
-        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons());
-        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons());
+        mNavMode = SysUINavigationMode.INSTANCE.get(mLauncher)
+                .addModeChangeListener(mNavigationModeChangeListener);
+        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(), mNavMode);
+        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(), mNavMode);
         mTaskbarContainerView.init(mTaskbarViewInApp);
         addToWindowManager();
         mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
@@ -278,6 +293,8 @@
         mHotseatController.cleanup();
 
         setWhichTaskbarViewIsVisible(null);
+        SysUINavigationMode.INSTANCE.get(mLauncher)
+                .removeModeChangeListener(mNavigationModeChangeListener);
     }
 
     private void removeFromWindowManager() {
@@ -315,6 +332,12 @@
         mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
     }
 
+    private void onNavModeChanged(SysUINavigationMode.Mode newMode) {
+        mNavMode = newMode;
+        cleanup();
+        init();
+    }
+
     /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
@@ -507,6 +530,7 @@
         /** Returns how much to scale non-icon elements such as spacing and dividers. */
         float getNonIconScale(TaskbarView taskbarView);
         void onItemPositionsChanged(TaskbarView taskbarView);
+        void onNavigationButtonClick(@TaskbarButton int buttonType);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
new file mode 100644
index 0000000..cf8ff38
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 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.launcher3.taskbar;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.IntDef;
+
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TouchInteractionService;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controller for 3 button mode in the taskbar.
+ * Handles all the functionality of the various buttons, making/routing the right calls into
+ * launcher or sysui/system.
+ *
+ * TODO: Create callbacks to hook into UI layer since state will change for more context buttons/
+ *       assistant invocation.
+ */
+public class TaskbarNavButtonController {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            BUTTON_BACK,
+            BUTTON_HOME,
+            BUTTON_RECENTS
+    })
+
+    public @interface TaskbarButton {}
+
+    static final int BUTTON_BACK = 1;
+    static final int BUTTON_HOME = BUTTON_BACK << 1;
+    static final int BUTTON_RECENTS = BUTTON_HOME << 1;
+
+
+    private final Context mContext;
+
+    public TaskbarNavButtonController(Context context) {
+        mContext = context;
+    }
+
+    public void onButtonClick(@TaskbarButton int buttonType) {
+        switch (buttonType) {
+            case BUTTON_BACK:
+                executeBack();
+                break;
+            case BUTTON_HOME:
+                navigateHome();
+                break;
+            case BUTTON_RECENTS:
+                navigateToOverview();;
+                break;
+        }
+    }
+
+    private void navigateHome() {
+        mContext.startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    private void navigateToOverview() {
+        TouchInteractionService.getInstance().getOverviewCommandHelper()
+                .addCommand(OverviewCommandHelper.TYPE_SHOW);
+    }
+
+    private void executeBack() {
+        SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
+    }
+
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 60a7add..5be25b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,6 +15,13 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
@@ -24,11 +31,14 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.view.DragEvent;
+import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 
 import androidx.annotation.LayoutRes;
@@ -45,12 +55,17 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.SysUINavigationMode;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
 public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
 
+
+    private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
+            SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
+
     private final int mIconTouchSize;
     private final boolean mIsRtl;
     private final int mTouchSlop;
@@ -69,6 +84,8 @@
     private int mHotseatStartIndex;
     private int mHotseatEndIndex;
 
+    private LinearLayout mButtonRegion;
+
     // Delegate touches to the closest view if within mIconTouchSize.
     private boolean mDelegateTargeted;
     private View mDelegateView;
@@ -77,6 +94,8 @@
     // Only non-null when the corresponding Folder is open.
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
 
+    private int mNavButtonStartIndex;
+
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -107,8 +126,18 @@
         mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
     }
 
-    protected void init(int numHotseatIcons) {
-        mHotseatStartIndex = 0;
+    protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) {
+        // TODO: check if buttons on left
+        if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) {
+            // 3 button
+            mNavButtonStartIndex = 0;
+            createNavButtons();
+        } else {
+            mNavButtonStartIndex = -1;
+            removeNavButtons();
+        }
+
+        mHotseatStartIndex = mNavButtonStartIndex + 1;
         mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
         updateHotseatItems(new ItemInfo[numHotseatIcons]);
 
@@ -185,11 +214,11 @@
             if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
                     || needsReinflate) {
                 removeView(hotseatView);
-                ActivityContext activityContext = ActivityContext.lookupContext(getContext());
+                ActivityContext activityContext = getActivityContext();
                 if (isFolder) {
                     FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
                     FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
-                            ActivityContext.lookupContext(getContext()), this, folderInfo);
+                            getActivityContext(), this, folderInfo);
                     folderIcon.setTextVisible(false);
                     hotseatView = folderIcon;
                 } else {
@@ -335,12 +364,71 @@
         return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
     }
 
+    private void removeNavButtons() {
+        if (mButtonRegion != null) {
+            mButtonRegion.removeAllViews();
+            removeView(mButtonRegion);
+        } // else We've never been in 3 button. Woah Scoob!
+    }
+
+    /**
+     * Add back/home/recents buttons into a single ViewGroup that will be inserted at
+     * {@param navButtonStartIndex}
+     */
+    private void createNavButtons() {
+        ActivityContext context = getActivityContext();
+        if (mButtonRegion == null) {
+            mButtonRegion = new LinearLayout(getContext());
+        } else {
+            mButtonRegion.removeAllViews();
+        }
+        mButtonRegion.setVisibility(VISIBLE);
+        LayoutParams regionParams = new LayoutParams(WRAP_CONTENT, MATCH_PARENT);
+        regionParams.gravity = Gravity.START; // check left/right preference
+        regionParams.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+
+        LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
+                context.getDeviceProfile().iconSizePx,
+                context.getDeviceProfile().iconSizePx
+        );
+        buttonParams.gravity = Gravity.CENTER;
+
+        // Back button
+        ImageView backButton = new ImageView(getContext());
+        backButton.setImageResource(R.drawable.ic_sysbar_back);
+        backButton.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
+        backButton.setPadding(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+        backButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_BACK));
+        mButtonRegion.addView(backButton, buttonParams);
+
+        // Home button
+        ImageView homeButton = new ImageView(getContext());
+        homeButton.setImageResource(R.drawable.ic_sysbar_home);
+        homeButton.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
+        homeButton.setPadding(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+        homeButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_HOME));
+        mButtonRegion.addView(homeButton, buttonParams);
+
+        // Recents button
+        ImageView recentsButton = new ImageView(getContext());
+        recentsButton.setImageResource(R.drawable.ic_sysbar_recent);
+        recentsButton.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
+        recentsButton.setPadding(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+        recentsButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_RECENTS));
+        mButtonRegion.addView(recentsButton, buttonParams);
+
+        addView(mButtonRegion, mNavButtonStartIndex);
+    }
+
     @Override
     public boolean onDragEvent(DragEvent event) {
         switch (event.getAction()) {
             case DragEvent.ACTION_DRAG_STARTED:
                 mIsDraggingItem = true;
-                AbstractFloatingView.closeAllOpenViews(ActivityContext.lookupContext(getContext()));
+                AbstractFloatingView.closeAllOpenViews(getActivityContext());
                 return true;
             case DragEvent.ACTION_DRAG_ENDED:
                 mIsDraggingItem = false;
@@ -407,12 +495,15 @@
     }
 
     private View inflate(@LayoutRes int layoutResId) {
-        return ActivityContext.lookupContext(getContext()).getLayoutInflater()
-                .inflate(layoutResId, this, false);
+        return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false);
     }
 
     @Override
     public void setInsets(Rect insets) {
         // Ignore, we just implement Insettable to draw behind system insets.
     }
+
+    private <T extends Context & ActivityContext> T getActivityContext() {
+        return ActivityContext.lookupContext(getContext());
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index efec037..74f4bea 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -34,6 +34,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Observer for the resource config that specifies the navigation bar mode.
@@ -75,7 +76,8 @@
     private int mNavBarGesturalHeight;
     private int mNavBarLargerGesturalHeight;
 
-    private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
+    private final List<NavigationModeChangeListener> mChangeListeners =
+            new CopyOnWriteArrayList<>();
     private final List<OneHandedModeChangeListener> mOneHandedOverlayChangeListeners =
             new ArrayList<>();
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4fc9770..afda3b9 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -259,6 +259,7 @@
     };
 
     private static boolean sConnected = false;
+    private static TouchInteractionService sInstance;
     private static boolean sIsInitialized = false;
     private RotationTouchHelper mRotationTouchHelper;
 
@@ -266,6 +267,11 @@
         return sConnected;
     }
 
+    @Nullable
+    public static TouchInteractionService getInstance() {
+        return sInstance;
+    }
+
     public static boolean isInitialized() {
         return sIsInitialized;
     }
@@ -293,6 +299,10 @@
 
     private DisplayManager mDisplayManager;
 
+    public TouchInteractionService() {
+        sInstance = this;
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -389,6 +399,10 @@
         onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
     }
 
+    public OverviewCommandHelper getOverviewCommandHelper() {
+        return mOverviewCommandHelper;
+    }
+
     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
         if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation