1 /*
<lambda>null2  * Copyright (C) 2023 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.haptics.slider
18 
19 import kotlinx.coroutines.CoroutineScope
20 import kotlinx.coroutines.Job
21 import kotlinx.coroutines.cancel
22 import kotlinx.coroutines.launch
23 
24 /**
25  * Tracker component for a slider.
26  *
27  * The tracker maintains a state machine operated by slider events coming from a
28  * [SliderEventProducer]. An action is executed in each state via a [SliderListener].
29  *
30  * @property[scope] [CoroutineScope] to launch the collection of [SliderEvent] and state machine
31  *   logic.
32  * @property[sliderListener] [SliderListener] to execute actions on a given [SliderState].
33  * @property[eventProducer] Producer of [SliderEvent] to iterate over a state machine.
34  */
35 sealed class SliderTracker(
36     protected val scope: CoroutineScope,
37     protected val sliderListener: SliderStateListener,
38     protected val eventProducer: SliderEventProducer,
39 ) {
40 
41     /* Reference to the current state of the internal state machine */
42     var currentState: SliderState = SliderState.IDLE
43         protected set
44 
45     /**
46      * Job that launches and maintains the coroutine that collects events and operates the state
47      * machine.
48      */
49     protected var job: Job? = null
50 
51     /** Indicator that the tracker is active and tracking */
52     var isTracking = false
53         get() = job != null && job?.isActive == true
54         private set
55 
56     /** Starts the [Job] that collects slider events and runs the state machine */
57     fun startTracking() {
58         job =
59             scope.launch {
60                 eventProducer.produceEvents().collect { event ->
61                     iterateState(event)
62                     executeOnState(currentState)
63                 }
64             }
65     }
66 
67     /** Stops the collection of slider events and the state machine */
68     fun stopTracking() {
69         job?.cancel("Stopped tracking slider state")
70         job = null
71         resetState()
72     }
73 
74     /**
75      * Iterate through the state machine due to a new slider event. As a result, the current state
76      * is modified.
77      *
78      * @param[event] The slider event that is received.
79      */
80     protected abstract suspend fun iterateState(event: SliderEvent)
81 
82     /**
83      * Execute an action based on the state of the state machine. This method should use the
84      * [SliderListener] to act on the current state.
85      *
86      * @param[currentState] A [SliderState] in the state machine
87      */
88     protected abstract fun executeOnState(currentState: SliderState)
89 
90     /** Reset the state machine by setting the current state to [SliderState.IDLE] */
91     protected open fun resetState() {
92         currentState = SliderState.IDLE
93     }
94 }
95