1 /*
2  * 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 android.widget.SeekBar
20 import android.widget.SeekBar.OnSeekBarChangeListener
21 import kotlinx.coroutines.flow.Flow
22 import kotlinx.coroutines.flow.MutableStateFlow
23 import kotlinx.coroutines.flow.asStateFlow
24 import kotlinx.coroutines.flow.update
25 
26 /** An event producer for a Seekable element such as the Android [SeekBar] */
27 class SeekableSliderEventProducer : SliderEventProducer, OnSeekBarChangeListener {
28 
29     /** The current event reported by a SeekBar */
30     private val _currentEvent = MutableStateFlow(SliderEvent(SliderEventType.NOTHING, 0f))
31 
32     override fun produceEvents(): Flow<SliderEvent> = _currentEvent.asStateFlow()
33 
34     override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
35         val eventType =
36             if (fromUser) SliderEventType.PROGRESS_CHANGE_BY_USER
37             else SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
38 
39         _currentEvent.value = SliderEvent(eventType, normalizeProgress(seekBar, progress))
40     }
41 
42     /**
43      * Normalize the integer progress of a SeekBar to the range from 0F to 1F.
44      *
45      * @param[seekBar] The SeekBar that reports a progress.
46      * @param[progress] The integer progress of the SeekBar within its min and max values.
47      * @return The progress in the range from 0F to 1F.
48      */
49     private fun normalizeProgress(seekBar: SeekBar, progress: Int): Float {
50         if (seekBar.max == seekBar.min) {
51             return 1.0f
52         }
53         val range = seekBar.max - seekBar.min
54         return (progress - seekBar.min) / range.toFloat()
55     }
56 
57     override fun onStartTrackingTouch(seekBar: SeekBar) {
58         _currentEvent.update { previousEvent ->
59             SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, previousEvent.currentProgress)
60         }
61     }
62 
63     override fun onStopTrackingTouch(seekBar: SeekBar) {
64         _currentEvent.update { previousEvent ->
65             SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, previousEvent.currentProgress)
66         }
67     }
68 }
69