1 /*
<lambda>null2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.systemui.inputdevice.tutorial.ui.view
18
19 import android.os.Bundle
20 import android.view.WindowManager
21 import androidx.activity.ComponentActivity
22 import androidx.activity.compose.setContent
23 import androidx.activity.enableEdgeToEdge
24 import androidx.activity.viewModels
25 import androidx.compose.runtime.Composable
26 import androidx.compose.runtime.getValue
27 import androidx.lifecycle.Lifecycle.State.STARTED
28 import androidx.lifecycle.compose.collectAsStateWithLifecycle
29 import androidx.lifecycle.lifecycleScope
30 import com.android.app.tracing.coroutines.launchTraced as launch
31 import com.android.compose.theme.PlatformTheme
32 import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
33 import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext
34 import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialMetricsLogger
35 import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
36 import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor
37 import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType
38 import com.android.systemui.inputdevice.tutorial.ui.composable.ActionKeyTutorialScreen
39 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel
40 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel.Factory.ViewModelFactoryAssistedProvider
41 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY
42 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE
43 import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE
44 import java.util.Optional
45 import javax.inject.Inject
46
47 /**
48 * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that
49 * either of them are actually not connected when this is launched
50 */
51 class KeyboardTouchpadTutorialActivity
52 @Inject
53 constructor(
54 private val viewModelFactoryAssistedProvider: ViewModelFactoryAssistedProvider,
55 private val touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
56 private val schedulerInteractor: TutorialSchedulerInteractor,
57 private val logger: InputDeviceTutorialLogger,
58 private val metricsLogger: KeyboardTouchpadTutorialMetricsLogger,
59 ) : ComponentActivity() {
60
61 companion object {
62 const val INTENT_TUTORIAL_SCOPE_KEY = "tutorial_scope"
63 const val INTENT_TUTORIAL_SCOPE_TOUCHPAD = "touchpad"
64 const val INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK = "touchpad_back"
65 const val INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME = "touchpad_home"
66 const val INTENT_TUTORIAL_SCOPE_KEYBOARD = "keyboard"
67 const val INTENT_TUTORIAL_SCOPE_ALL = "all"
68
69 const val INTENT_TUTORIAL_ENTRY_POINT_KEY = "entry_point"
70 const val INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER = "scheduler"
71 const val INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU = "contextual_edu"
72 }
73
74 private val vm by
75 viewModels<KeyboardTouchpadTutorialViewModel>(
76 factoryProducer = {
77 viewModelFactoryAssistedProvider.create(touchpadTutorialScreensProvider.isPresent)
78 }
79 )
80
81 override fun onCreate(savedInstanceState: Bundle?) {
82 super.onCreate(savedInstanceState)
83 enableEdgeToEdge()
84 // required to handle 3+ fingers on touchpad
85 window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
86 window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS)
87 lifecycle.addObserver(vm)
88 lifecycleScope.launch {
89 vm.closeActivity.collect { finish ->
90 if (finish) {
91 logger.logCloseTutorial(TutorialContext.KEYBOARD_TOUCHPAD_TUTORIAL)
92 finish()
93 }
94 }
95 }
96 val entryPointExtra = intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY)
97 val isAutoProceed =
98 if (entryPointExtra == null) true
99 else entryPointExtra.equals(INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER)
100 val scopeExtra = intent.getStringExtra(INTENT_TUTORIAL_SCOPE_KEY)
101 val isScopeAll = INTENT_TUTORIAL_SCOPE_ALL.equals(scopeExtra)
102 setContent {
103 PlatformTheme {
104 KeyboardTouchpadTutorialContainer(
105 vm,
106 touchpadTutorialScreensProvider,
107 isAutoProceed,
108 isScopeAll,
109 )
110 }
111 }
112 if (savedInstanceState == null) {
113 logger.logOpenTutorial(TutorialContext.KEYBOARD_TOUCHPAD_TUTORIAL)
114
115 val tutorialTypeExtra = intent.getStringExtra(INTENT_TUTORIAL_SCOPE_KEY)
116 metricsLogger.logPeripheralTutorialLaunched(entryPointExtra, tutorialTypeExtra)
117 // We only update launched info when the tutorial is triggered by the scheduler
118 if (INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER.equals(entryPointExtra))
119 updateLaunchInfo(tutorialTypeExtra)
120 }
121 }
122
123 private fun updateLaunchInfo(tutorialTypeExtra: String?) {
124 val type =
125 when (tutorialTypeExtra) {
126 INTENT_TUTORIAL_SCOPE_KEYBOARD -> TutorialType.KEYBOARD
127 INTENT_TUTORIAL_SCOPE_TOUCHPAD -> TutorialType.TOUCHPAD
128 INTENT_TUTORIAL_SCOPE_ALL -> TutorialType.BOTH
129 else -> TutorialType.NONE
130 }
131 schedulerInteractor.updateLaunchInfo(type)
132 }
133 }
134
135 @Composable
KeyboardTouchpadTutorialContainernull136 fun KeyboardTouchpadTutorialContainer(
137 vm: KeyboardTouchpadTutorialViewModel,
138 touchpadScreens: Optional<TouchpadTutorialScreensProvider>,
139 isAutoProceed: Boolean = false,
140 isScopeAll: Boolean = false,
141 ) {
142 val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED)
143 when (activeScreen) {
144 BACK_GESTURE ->
145 touchpadScreens
146 .get()
147 .BackGesture(
148 onDoneButtonClicked = vm::onDoneButtonClicked,
149 onBack = vm::onBack,
150 onAutoProceed = if (isAutoProceed) vm::onAutoProceed else null,
151 )
152 HOME_GESTURE ->
153 touchpadScreens
154 .get()
155 .HomeGesture(
156 onDoneButtonClicked = vm::onDoneButtonClicked,
157 onBack = vm::onBack,
158 onAutoProceed = if (isScopeAll) vm::onAutoProceed else null,
159 )
160 ACTION_KEY ->
161 ActionKeyTutorialScreen(
162 onDoneButtonClicked = vm::onDoneButtonClicked,
163 onBack = vm::onBack,
164 )
165 }
166 }
167