Skip to content

Commit

Permalink
[Android] virtual-device-app: Implement doorlock viewmodel (#30236)
Browse files Browse the repository at this point in the history
* virtual-device-app: Implement doorlock viewmodel

Signed-off-by: Jaehoon You <[email protected]>
Signed-off-by: Charles Kim <[email protected]>

* virtual-device-app: Apply 'LockState' enum type to align with Doorlock cluster specification

Signed-off-by: Jaehoon You <[email protected]>
Signed-off-by: Charles Kim <[email protected]>

* virtual-device-app: Change variable name more obviously

Signed-off-by: Jaehoon You <[email protected]>
Signed-off-by: Charles Kim <[email protected]>

* virtual-device-app: Fix initial value mismatching

Signed-off-by: Jaehoon You <[email protected]>
Signed-off-by: Charles Kim <[email protected]>

---------

Signed-off-by: Jaehoon You <[email protected]>
Signed-off-by: Charles Kim <[email protected]>
  • Loading branch information
Jaehoon-You authored Nov 23, 2023
1 parent a454ccd commit fc2a071
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.matter.virtual.device.app.core.data.repository.cluster

import com.matter.virtual.device.app.core.model.matter.LockState
import kotlinx.coroutines.flow.StateFlow

interface DoorLockManagerRepository {
fun getLockStateFlow(): StateFlow<Boolean>
fun getLockStateFlow(): StateFlow<LockState>

suspend fun setLockState(value: Boolean)
suspend fun setLockState(lockState: LockState)

suspend fun sendLockAlarmEvent()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.matter.virtual.device.app.core.data.repository.cluster

import com.matter.virtual.device.app.core.matter.manager.DoorLockManagerStub
import com.matter.virtual.device.app.core.model.matter.LockState
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
import timber.log.Timber
Expand All @@ -9,13 +10,13 @@ internal class DoorLockManagerRepositoryImpl
@Inject
constructor(private val doorLockManagerStub: DoorLockManagerStub) : DoorLockManagerRepository {

override fun getLockStateFlow(): StateFlow<Boolean> {
override fun getLockStateFlow(): StateFlow<LockState> {
return doorLockManagerStub.lockState
}

override suspend fun setLockState(value: Boolean) {
Timber.d("setLockState():$value")
doorLockManagerStub.setLockState(value)
override suspend fun setLockState(lockState: LockState) {
Timber.d("setLockState():$lockState")
doorLockManagerStub.setLockState(lockState)
}

override suspend fun sendLockAlarmEvent() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.matter.virtual.device.app.core.domain.usecase.matter.cluster.doorlock

import com.matter.virtual.device.app.core.data.repository.cluster.DoorLockManagerRepository
import com.matter.virtual.device.app.core.model.matter.LockState
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow

class GetLockStateFlowUseCase
@Inject
constructor(private val doorLockManagerRepository: DoorLockManagerRepository) {

operator fun invoke(): StateFlow<Boolean> = doorLockManagerRepository.getLockStateFlow()
operator fun invoke(): StateFlow<LockState> = doorLockManagerRepository.getLockStateFlow()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.matter.virtual.device.app.core.domain.usecase.matter.cluster.doorloc
import com.matter.virtual.device.app.core.common.di.IoDispatcher
import com.matter.virtual.device.app.core.data.repository.cluster.DoorLockManagerRepository
import com.matter.virtual.device.app.core.domain.CoroutineUseCase
import com.matter.virtual.device.app.core.model.matter.LockState
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher

Expand All @@ -11,9 +12,9 @@ class SetLockStateUseCase
constructor(
private val doorLockManagerRepository: DoorLockManagerRepository,
@IoDispatcher dispatcher: CoroutineDispatcher
) : CoroutineUseCase<Boolean, Unit>(dispatcher) {
) : CoroutineUseCase<LockState, Unit>(dispatcher) {

override suspend fun execute(param: Boolean) {
override suspend fun execute(param: LockState) {
doorLockManagerRepository.setLockState(param)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.matter.virtual.device.app.core.matter.manager
import com.matter.virtual.device.app.DeviceApp
import com.matter.virtual.device.app.DoorLockManager
import com.matter.virtual.device.app.core.common.MatterConstants
import com.matter.virtual.device.app.core.model.matter.LockState
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -12,14 +13,14 @@ import timber.log.Timber
@Singleton
class DoorLockManagerStub @Inject constructor(private val deviceApp: DeviceApp) : DoorLockManager {

private val _lockState = MutableStateFlow(false)
val lockState: StateFlow<Boolean>
private val _lockState = MutableStateFlow(LockState.LOCKED)
val lockState: StateFlow<LockState>
get() = _lockState

override fun initAttributeValue() {
Timber.d("initAttributeValue()")
deviceApp.setLockType(MatterConstants.DEFAULT_ENDPOINT, DoorLockManager.DlLockType_kMagnetic)
deviceApp.setLockState(MatterConstants.DEFAULT_ENDPOINT, lockState.value.asLockState())
deviceApp.setLockState(MatterConstants.DEFAULT_ENDPOINT, DoorLockManager.DlLockState_kLocked)
deviceApp.setActuatorEnabled(MatterConstants.DEFAULT_ENDPOINT, true)
deviceApp.setOperatingMode(
MatterConstants.DEFAULT_ENDPOINT,
Expand All @@ -33,29 +34,34 @@ class DoorLockManagerStub @Inject constructor(private val deviceApp: DeviceApp)

override fun handleLockStateChanged(value: Int) {
Timber.d("handleLockStateChanged():$value")
_lockState.value = value.asBooleanLockState()
_lockState.value = value.asLockStateEnum()
}

fun setLockState(value: Boolean) {
Timber.d("setLockState():$value")
deviceApp.setLockState(MatterConstants.DEFAULT_ENDPOINT, value.asLockState())
fun setLockState(lockState: LockState) {
Timber.d("setLockState():$lockState")
deviceApp.setLockState(MatterConstants.DEFAULT_ENDPOINT, lockState.asLockState())
}

fun sendLockAlarmEvent() {
Timber.d("sendLockAlarmEvent()")
deviceApp.sendLockAlarmEvent(MatterConstants.DEFAULT_ENDPOINT)
}

private fun Boolean.asLockState() =
private fun LockState.asLockState() =
when (this) {
true -> DoorLockManager.DlLockState_kUnlocked
false -> DoorLockManager.DlLockState_kLocked
LockState.NOT_FULLY_LOCKED -> DoorLockManager.DlLockState_kNotFullyLocked
LockState.LOCKED -> DoorLockManager.DlLockState_kLocked
LockState.UNLOCKED -> DoorLockManager.DlLockState_kUnlocked
LockState.UNLATCHED -> DoorLockManager.DlLockState_kUnlatched
else -> DoorLockManager.DlLockState_kUnknownEnumValue
}

private fun Int.asBooleanLockState() =
private fun Int.asLockStateEnum() =
when (this) {
DoorLockManager.DlLockState_kUnlocked -> true
DoorLockManager.DlLockState_kLocked -> false
else -> false
DoorLockManager.DlLockState_kNotFullyLocked -> LockState.NOT_FULLY_LOCKED
DoorLockManager.DlLockState_kLocked -> LockState.LOCKED
DoorLockManager.DlLockState_kUnlocked -> LockState.UNLOCKED
DoorLockManager.DlLockState_kUnlatched -> LockState.UNLATCHED
else -> LockState.UNKNOWN_ENUM_VALUE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.matter.virtual.device.app.core.model.matter

enum class LockState {
NOT_FULLY_LOCKED,
LOCKED,
UNLOCKED,
UNLATCHED,
UNKNOWN_ENUM_VALUE
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ dependencies {

implementation(project(":core:common"))
implementation(project(":core:domain"))
implementation(project(":core:model"))
implementation(project(":core:ui"))

implementation(Deps.AndroidX.core)
implementation(Deps.AndroidX.appcompat)
implementation(Deps.AndroidX.fragment)
implementation(Deps.AndroidX.Lifecycle.viewmodel)
implementation(Deps.AndroidX.Lifecycle.livedata)

implementation(Deps.Kotlin.serialization)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,100 @@
package com.matter.virtual.device.app.feature.closure

import androidx.lifecycle.LiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.matter.virtual.device.app.core.common.successOr
import com.matter.virtual.device.app.core.domain.usecase.matter.IsFabricRemovedUseCase
import com.matter.virtual.device.app.core.domain.usecase.matter.StartMatterAppServiceUseCase
import com.matter.virtual.device.app.core.domain.usecase.matter.cluster.doorlock.GetLockStateFlowUseCase
import com.matter.virtual.device.app.core.domain.usecase.matter.cluster.doorlock.SendLockAlarmEventUseCase
import com.matter.virtual.device.app.core.domain.usecase.matter.cluster.doorlock.SetLockStateUseCase
import com.matter.virtual.device.app.core.domain.usecase.matter.cluster.powersource.GetBatPercentRemainingUseCase
import com.matter.virtual.device.app.core.domain.usecase.matter.cluster.powersource.SetBatPercentRemainingUseCase
import com.matter.virtual.device.app.core.model.matter.LockState
import com.matter.virtual.device.app.core.ui.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import timber.log.Timber

@HiltViewModel
class DoorLockViewModel @Inject constructor(savedStateHandle: SavedStateHandle) :
BaseViewModel(savedStateHandle) {
class DoorLockViewModel
@Inject
constructor(
getLockStateFlowUseCase: GetLockStateFlowUseCase,
getBatPercentRemainingUseCase: GetBatPercentRemainingUseCase,
private val setLockStateUseCase: SetLockStateUseCase,
private val sendLockAlarmEventUseCase: SendLockAlarmEventUseCase,
private val setBatPercentRemainingUseCase: SetBatPercentRemainingUseCase,
private val startMatterAppServiceUseCase: StartMatterAppServiceUseCase,
private val isFabricRemovedUseCase: IsFabricRemovedUseCase,
savedStateHandle: SavedStateHandle
) : BaseViewModel(savedStateHandle) {

private val _lockState: StateFlow<LockState> = getLockStateFlowUseCase()
val lockState: LiveData<LockState>
get() = _lockState.asLiveData()

private val _batteryRemainingPercentage: MutableStateFlow<Int> =
getBatPercentRemainingUseCase() as MutableStateFlow<Int>
val batteryRemainingPercentage: LiveData<Int>
get() = _batteryRemainingPercentage.asLiveData()

init {
viewModelScope.launch { startMatterAppServiceUseCase(matterSettings) }

viewModelScope.launch {
val isFabricRemoved = isFabricRemovedUseCase().successOr(false)
if (isFabricRemoved) {
Timber.d("Fabric Removed")
onFabricRemoved()
}
}
}

override fun onCleared() {
Timber.d("onCleared()")
super.onCleared()
}

fun onClickButton() {
Timber.d("onClickButton()")
viewModelScope.launch {
Timber.d("current lockState value = ${_lockState.value}")
if (_lockState.value == LockState.LOCKED) {
Timber.d("set value = unlocked")
setLockStateUseCase(LockState.UNLOCKED)
} else {
Timber.d("set value = locked")
setLockStateUseCase(LockState.LOCKED)
}
}
}

fun onClickSendLockAlarmEventButton() {
Timber.d("onClickSendLockAlarmEventButton()")
viewModelScope.launch {
if (_lockState.value == LockState.LOCKED) {
// if lockState == locked, send alarm event and change the lockState to unlocked
sendLockAlarmEventUseCase()
setLockStateUseCase(LockState.UNLOCKED)
}
}
}

fun updateBatterySeekbarProgress(progress: Int) {
_batteryRemainingPercentage.value = progress
}

fun updateBatteryStatusToCluster(progress: Int) {
Timber.d("progress:$progress")
viewModelScope.launch {
updateBatterySeekbarProgress(progress)
setBatPercentRemainingUseCase(progress)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public interface DoorLockManager {
int DlLockState_kLocked = 1;
int DlLockState_kUnlocked = 2;
int DlLockState_kUnlatched = 3;
int DlLockState_kUnknownEnumValue = 4;

int DlLockType_kDeadBolt = 0;
int DlLockType_kMagnetic = 1;
Expand Down

0 comments on commit fc2a071

Please sign in to comment.