[Contextual Edu] Enable updating Edu data in Launcher

- Added ContextualEduStatsManager (in Launcher3) and SystemContextualEduStatsManager(in Quickstep) to enable classes in Launcher3 and quickstep to update contextual edu data
- Implemented new updateContextualEduData method in SystemUiProxy file, so the EduStatsManager could use it for update
- AbsSwipeUpHandler handles the logic to go home/overview when in app. Added code to update contextual edu data when these action is triggered.

Test: LauncherSwipeHandlerV2Test
Bug: 357542123
Flag: com.android.systemui.keyboard_touchpad_contextual_education
Change-Id: I6fc5a285ba1a1d770c54cc7af444ff8b3051bd00
diff --git a/Android.bp b/Android.bp
index ba04bb3..def024e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -231,6 +231,7 @@
         "androidx.preference_preference",
         "SystemUISharedLib",
         "//frameworks/libs/systemui:animationlib",
+        "//frameworks/libs/systemui:contextualeducationlib",
         "launcher-testing-shared",
     ],
     srcs: [
@@ -311,6 +312,7 @@
         "//frameworks/libs/systemui:iconloader_base",
         "//frameworks/libs/systemui:view_capture",
         "//frameworks/libs/systemui:animationlib",
+        "//frameworks/libs/systemui:contextualeducationlib",
         "SystemUI-statsd",
         "launcher-testing-shared",
         "androidx.lifecycle_lifecycle-common-java8",
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index fd12210..e8cb5d5 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -35,7 +35,7 @@
     <string name="launcher_restore_event_logger_class" translatable="false">com.android.quickstep.LauncherRestoreEventLoggerImpl</string>
     <string name="plugin_manager_wrapper_class" translatable="false">com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl</string>
     <string name="taskbar_edu_tooltip_controller_class" translatable="false">com.android.launcher3.taskbar.TaskbarEduTooltipController</string>
-
+    <string name="contextual_edu_manager_class" translatable="false">com.android.quickstep.contextualeducation.SystemContextualEduStatsManager</string>
     <string name="nav_handle_long_press_handler_class" translatable="false"></string>
     <string name="assist_utils_class" translatable="false"></string>
     <string name="assist_state_manager_class" translatable="false"></string>
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4dc04e7..540ab37 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -105,6 +105,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
@@ -140,6 +141,7 @@
 import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskContainer;
 import com.android.quickstep.views.TaskView;
+import com.android.systemui.contextualeducation.GestureType;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -1408,6 +1410,8 @@
             if (mRecentsAnimationController != null) {
                 mRecentsAnimationController.detachNavigationBarFromApp(true);
             }
+            ContextualEduStatsManager.INSTANCE.get(mContext).updateEduStats(
+                    mGestureState.isTrackpadGesture(), GestureType.HOME);
         } else if (endTarget == RECENTS) {
             if (mRecentsView != null) {
                 int nearestPage = mRecentsView.getDestinationPage();
@@ -1432,6 +1436,8 @@
                 if (!mGestureState.isHandlingAtomicEvent() || isScrolling) {
                     duration = Math.max(duration, mRecentsView.getScroller().getDuration());
                 }
+                ContextualEduStatsManager.INSTANCE.get(mContext).updateEduStats(
+                        mGestureState.isTrackpadGesture(), GestureType.OVERVIEW);
             }
         } else if (endTarget == LAST_TASK && mRecentsView != null
                 && mRecentsView.getNextPage() != mRecentsView.getRunningTaskIndex()) {
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0c2f29b..adffb5b 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -240,6 +240,17 @@
     }
 
     @Override
+    public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.updateContextualEduStats(isTrackpadGesture, gestureType);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call updateContextualEduStats");
+            }
+        }
+    }
+
+    @Override
     public void setHomeRotationEnabled(boolean enabled) {
         if (mSystemUiProxy != null) {
             try {
diff --git a/quickstep/src/com/android/quickstep/contextualeducation/SystemContextualEduStatsManager.java b/quickstep/src/com/android/quickstep/contextualeducation/SystemContextualEduStatsManager.java
new file mode 100644
index 0000000..d470b88
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/contextualeducation/SystemContextualEduStatsManager.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 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.contextualeducation;
+
+import android.content.Context;
+
+import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.contextualeducation.GestureType;
+
+/**
+ * A class to update contextual education data via {@link SystemUiProxy}
+ */
+public class SystemContextualEduStatsManager extends ContextualEduStatsManager {
+    private Context mContext;
+
+    public SystemContextualEduStatsManager(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void updateEduStats(boolean isTrackpadGesture, GestureType gestureType) {
+        SystemUiProxy.INSTANCE.get(mContext).updateContextualEduStats(isTrackpadGesture,
+                gestureType.name());
+    }
+
+    @Override
+    public void close() {
+    }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
new file mode 100644
index 0000000..1f88743
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2024 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
+
+import android.graphics.PointF
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.R
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.shared.system.InputConsumerController
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LauncherSwipeHandlerV2Test {
+
+    @Mock private lateinit var taskAnimationManager: TaskAnimationManager
+
+    private lateinit var gestureState: GestureState
+    @Mock private lateinit var inputConsumerController: InputConsumerController
+
+    @Mock private lateinit var systemUiProxy: SystemUiProxy
+
+    private lateinit var underTest: LauncherSwipeHandlerV2
+
+    @get:Rule val mockitoRule = MockitoJUnit.rule()
+
+    private val launcherModelHelper = LauncherModelHelper()
+    private val sandboxContext = launcherModelHelper.sandboxContext
+
+    private val flingSpeed =
+        -(sandboxContext.resources.getDimension(R.dimen.quickstep_fling_threshold_speed) + 1)
+
+    @Before
+    fun setup() {
+        sandboxContext.putObject(SystemUiProxy.INSTANCE, systemUiProxy)
+        val deviceState = mock(RecentsAnimationDeviceState::class.java)
+        whenever(deviceState.rotationTouchHelper).thenReturn(mock(RotationTouchHelper::class.java))
+        gestureState = spy(GestureState(OverviewComponentObserver(sandboxContext, deviceState), 0))
+
+        underTest =
+            LauncherSwipeHandlerV2(
+                sandboxContext,
+                deviceState,
+                taskAnimationManager,
+                gestureState,
+                0,
+                false,
+                inputConsumerController
+            )
+        underTest.onGestureStarted(/* isLikelyToStartNewTask= */ false)
+    }
+
+    @Test
+    fun goHomeFromAppByTrackpad_updateEduStats() {
+        gestureState.setTrackpadGestureType(GestureState.TrackpadGestureType.THREE_FINGER)
+        underTest.onGestureEnded(flingSpeed, PointF())
+        verify(systemUiProxy)
+            .updateContextualEduStats(
+                /* isTrackpadGesture= */ eq(true),
+                eq(GestureType.HOME.toString())
+            )
+    }
+
+    @Test
+    fun goHomeFromAppByTouch_updateEduStats() {
+        underTest.onGestureEnded(flingSpeed, PointF())
+        verify(systemUiProxy)
+            .updateContextualEduStats(
+                /* isTrackpadGesture= */ eq(false),
+                eq(GestureType.HOME.toString())
+            )
+    }
+}
diff --git a/res/values/config.xml b/res/values/config.xml
index 2a3b588..507ce9a 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -77,6 +77,7 @@
     <string name="taskbar_view_callbacks_factory_class" translatable="false"></string>
     <string name="launcher_restore_event_logger_class" translatable="false"></string>
     <string name="taskbar_edu_tooltip_controller_class" translatable="false"></string>
+    <string name="contextual_edu_manager_class" translatable="false"></string>
     <!--  Used for determining category of a widget presented in widget recommendations. -->
     <string name="widget_recommendation_category_provider_class" translatable="false"></string>
     <string name="api_wrapper_class" translatable="false"></string>
diff --git a/src/com/android/launcher3/contextualeducation/ContextualEduStatsManager.java b/src/com/android/launcher3/contextualeducation/ContextualEduStatsManager.java
new file mode 100644
index 0000000..da13546
--- /dev/null
+++ b/src/com/android/launcher3/contextualeducation/ContextualEduStatsManager.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 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.contextualeducation;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.SafeCloseable;
+import com.android.systemui.contextualeducation.GestureType;
+
+/**
+ * A class to update contextual education data. It is a no-op implementation and could be
+ * overridden by changing the resource value [R.string.contextual_edu_manager_class] to provide
+ * a real implementation.
+ */
+public class ContextualEduStatsManager implements ResourceBasedOverride, SafeCloseable {
+    public static final MainThreadInitializedObject<ContextualEduStatsManager> INSTANCE =
+            forOverride(ContextualEduStatsManager.class, R.string.contextual_edu_manager_class);
+
+    /**
+     * Updates contextual education stats when a gesture is triggered
+     * @param isTrackpadGesture indicates if the gesture is triggered by trackpad
+     * @param gestureType type of gesture triggered
+     */
+    public void updateEduStats(boolean isTrackpadGesture, GestureType gestureType) {
+    }
+
+    @Override
+    public void close() {
+    }
+}