blob: 3a21b809ec30094cfcf390d0190d815c3439251e [file] [log] [blame]
/*
* Copyright (C) 2021 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.unfold.updates
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import androidx.core.util.Consumer
import com.android.unfold.updates.screen.ScreenStatusProvider
import com.android.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener
import com.android.unfold.updates.hinge.FULLY_OPEN_DEGREES
import com.android.unfold.updates.hinge.HingeAngleProvider
import java.util.concurrent.Executor
internal class DeviceFoldStateProvider(
context: Context,
private val hingeAngleProvider: HingeAngleProvider,
private val screenStatusProvider: ScreenStatusProvider,
private val deviceStateManager: DeviceStateManager,
private val mainExecutor: Executor
) : FoldStateProvider {
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
@FoldUpdate
private var lastFoldUpdate: Int? = null
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
private val foldStateListener = FoldStateListener(context)
private var isFolded = false
override fun start() {
deviceStateManager.registerCallback(
mainExecutor,
foldStateListener
)
screenStatusProvider.addCallback(screenListener)
hingeAngleProvider.addCallback(hingeAngleListener)
}
override fun stop() {
screenStatusProvider.removeCallback(screenListener)
deviceStateManager.unregisterCallback(foldStateListener)
hingeAngleProvider.removeCallback(hingeAngleListener)
hingeAngleProvider.stop()
}
override fun addCallback(listener: FoldUpdatesListener) {
outputListeners.add(listener)
}
override fun removeCallback(listener: FoldUpdatesListener) {
outputListeners.remove(listener)
}
private fun onHingeAngle(angle: Float) {
when (lastFoldUpdate) {
FOLD_UPDATE_FINISH_FULL_OPEN -> {
if (FULLY_OPEN_DEGREES - angle > MOVEMENT_THRESHOLD_DEGREES) {
lastFoldUpdate = FOLD_UPDATE_START_CLOSING
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
}
}
FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING -> {
if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
}
}
}
outputListeners.forEach { it.onHingeAngleUpdate(angle) }
}
private inner class FoldStateListener(context: Context) :
DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
isFolded = folded
if (folded) {
lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
hingeAngleProvider.stop()
} else {
lastFoldUpdate = FOLD_UPDATE_START_OPENING
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
hingeAngleProvider.start()
}
})
private inner class ScreenStatusListener :
ScreenStatusProvider.ScreenListener {
override fun onScreenTurnedOn() {
if (!isFolded) {
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }
}
}
}
private inner class HingeAngleListener : Consumer<Float> {
override fun accept(angle: Float) {
onHingeAngle(angle)
}
}
}
private const val MOVEMENT_THRESHOLD_DEGREES = 10f
private const val FULLY_OPEN_THRESHOLD_DEGREES = 10f