Schedule OOBE and handle disconnection
Bug: 344862874
Flag: com.android.systemui.shared.new_touchpad_gestures_tutorial
Test: manual
Change-Id: I20a646166b9542ec2023f75a2860be2dae39859c
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt
index dbfea76..701d3da 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadOobeTutorialCoreStartable.kt
@@ -18,7 +18,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.inputdevice.oobe.domain.interactor.OobeTutorialSchedulerInteractor
+import com.android.systemui.inputdevice.oobe.domain.interactor.OobeSchedulerInteractor
import com.android.systemui.shared.Flags.newTouchpadGesturesTutorial
import dagger.Lazy
import javax.inject.Inject
@@ -27,11 +27,10 @@
@SysUISingleton
class KeyboardTouchpadOobeTutorialCoreStartable
@Inject
-constructor(private val oobeTutorialSchedulerInteractor: Lazy<OobeTutorialSchedulerInteractor>) :
- CoreStartable {
+constructor(private val oobeSchedulerInteractor: Lazy<OobeSchedulerInteractor>) : CoreStartable {
override fun start() {
if (newTouchpadGesturesTutorial()) {
- oobeTutorialSchedulerInteractor.get().start()
+ oobeSchedulerInteractor.get().start()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt
new file mode 100644
index 0000000..e5aedc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/data/model/OobeSchedulerInfo.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 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.systemui.inputdevice.oobe.data.model
+
+data class OobeSchedulerInfo(
+ val keyboard: DeviceSchedulerInfo = DeviceSchedulerInfo(),
+ val touchpad: DeviceSchedulerInfo = DeviceSchedulerInfo()
+)
+
+data class DeviceSchedulerInfo(var isLaunched: Boolean = false, var connectionTime: Long? = null) {
+ val wasEverConnected: Boolean
+ get() = connectionTime != null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt
new file mode 100644
index 0000000..b014c08
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeSchedulerInteractor.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 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.systemui.inputdevice.oobe.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.inputdevice.oobe.data.model.DeviceSchedulerInfo
+import com.android.systemui.inputdevice.oobe.data.model.OobeSchedulerInfo
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import java.time.Duration
+import java.time.Instant
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+
+/**
+ * When the first time a keyboard or touchpad id connected, wait for [LAUNCH_DELAY], then launch the
+ * tutorial as soon as there's a connected device
+ */
+@SysUISingleton
+class OobeSchedulerInteractor
+@Inject
+constructor(
+ @Application private val context: Context,
+ @Application private val applicationScope: CoroutineScope,
+ private val keyboardRepository: KeyboardRepository,
+ private val touchpadRepository: TouchpadRepository
+) {
+ private val info = OobeSchedulerInfo()
+
+ fun start() {
+ if (!info.keyboard.isLaunched) {
+ applicationScope.launch {
+ schedule(keyboardRepository.isAnyKeyboardConnected, info.keyboard)
+ }
+ }
+ if (!info.touchpad.isLaunched) {
+ applicationScope.launch {
+ schedule(touchpadRepository.isAnyTouchpadConnected, info.touchpad)
+ }
+ }
+ }
+
+ private suspend fun schedule(isAnyDeviceConnected: Flow<Boolean>, info: DeviceSchedulerInfo) {
+ if (!info.wasEverConnected) {
+ waitForDeviceConnection(isAnyDeviceConnected)
+ info.connectionTime = Instant.now().toEpochMilli()
+ }
+ delay(remainingTimeMillis(info.connectionTime!!))
+ waitForDeviceConnection(isAnyDeviceConnected)
+ info.isLaunched = true
+ launchOobe()
+ }
+
+ private suspend fun waitForDeviceConnection(isAnyDeviceConnected: Flow<Boolean>): Boolean {
+ return isAnyDeviceConnected.filter { it }.first()
+ }
+
+ private fun launchOobe() {
+ val intent = Intent(TUTORIAL_ACTION)
+ intent.addCategory(Intent.CATEGORY_DEFAULT)
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(intent)
+ }
+
+ private fun remainingTimeMillis(start: Long): Long {
+ val elapsed = Instant.now().toEpochMilli() - start
+ return LAUNCH_DELAY - elapsed
+ }
+
+ companion object {
+ const val TAG = "OobeSchedulerInteractor"
+ const val TUTORIAL_ACTION = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
+ private val LAUNCH_DELAY = Duration.ofHours(72).toMillis()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeTutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeTutorialSchedulerInteractor.kt
deleted file mode 100644
index 0d69081..0000000
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/domain/interactor/OobeTutorialSchedulerInteractor.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 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.systemui.inputdevice.oobe.domain.interactor
-
-import android.content.Context
-import android.content.Intent
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyboard.data.repository.KeyboardRepository
-import com.android.systemui.touchpad.data.repository.TouchpadRepository
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-
-/** When keyboards or touchpads are connected, schedule a tutorial after given time has elapsed */
-@SysUISingleton
-class OobeTutorialSchedulerInteractor
-@Inject
-constructor(
- @Application private val context: Context,
- @Application private val applicationScope: CoroutineScope,
- keyboardRepository: KeyboardRepository,
- touchpadRepository: TouchpadRepository
-) {
- private val isAnyKeyboardConnected = keyboardRepository.isAnyKeyboardConnected
- private val isAnyTouchpadConnected = touchpadRepository.isAnyTouchpadConnected
-
- fun start() {
- applicationScope.launch { isAnyKeyboardConnected.collect { startOobe() } }
- applicationScope.launch { isAnyTouchpadConnected.collect { startOobe() } }
- }
-
- private fun startOobe() {
- val intent = Intent(TUTORIAL_ACTION)
- intent.addCategory(Intent.CATEGORY_DEFAULT)
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- context.startActivity(intent)
- }
-
- companion object {
- const val TAG = "OobeSchedulerInteractor"
- const val TUTORIAL_ACTION = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/TouchpadModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/TouchpadModule.kt
index c86ac2f..2f74d29 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/TouchpadModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/TouchpadModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.touchpad
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.touchpad.data.repository.TouchpadRepository
import com.android.systemui.touchpad.data.repository.TouchpadRepositoryImpl
import dagger.Binds
@@ -25,5 +26,6 @@
abstract class TouchpadModule {
@Binds
+ @SysUISingleton
abstract fun bindTouchpadRepository(repository: TouchpadRepositoryImpl): TouchpadRepository
}