Skip to content

Commit

Permalink
[Android] Implement Mode Select fragment in Chiptool (project-chip#36999
Browse files Browse the repository at this point in the history
)

* Implement Mode Select fragment

* Fix refactoring
  • Loading branch information
joonhaengHeo authored Jan 10, 2025
1 parent 522c555 commit 3f62505
Show file tree
Hide file tree
Showing 5 changed files with 540 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class SelectActionFragment : Fragment() {
binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() }
binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() }
binding.icdBtn.setOnClickListener { handleICDClicked() }
binding.modeSelectBtn.setOnClickListener { handleModeSelectClicked() }

return binding.root
}
Expand Down Expand Up @@ -255,6 +256,10 @@ class SelectActionFragment : Fragment() {
showFragment(ICDFragment.newInstance())
}

private fun handleModeSelectClicked() {
showFragment(ModeSelectClientFragment.newInstance())
}

companion object {

@JvmStatic fun newInstance() = SelectActionFragment()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
package com.google.chip.chiptool.clusterclient

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipClusters
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ClusterIDMapping
import chip.devicecontroller.ClusterIDMapping.ModeSelect
import chip.devicecontroller.ReportCallback
import chip.devicecontroller.WriteAttributesCallback
import chip.devicecontroller.cluster.structs.ModeSelectClusterModeOptionStruct
import chip.devicecontroller.model.AttributeState
import chip.devicecontroller.model.AttributeWriteRequest
import chip.devicecontroller.model.ChipAttributePath
import chip.devicecontroller.model.ChipEventPath
import chip.devicecontroller.model.ChipPathId
import chip.devicecontroller.model.NodeState
import chip.devicecontroller.model.Status
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.R
import com.google.chip.chiptool.databinding.ModeSelectFragmentBinding
import java.util.Optional
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import matter.tlv.AnonymousTag
import matter.tlv.TlvReader
import matter.tlv.TlvWriter

class ModeSelectClientFragment : Fragment() {
private val deviceController: ChipDeviceController
get() = ChipClient.getDeviceController(requireContext())

private lateinit var scope: CoroutineScope

private lateinit var addressUpdateFragment: AddressUpdateFragment

private var _binding: ModeSelectFragmentBinding? = null

private val startUpMode: UInt
get() = binding.startUpModeEd.text.toString().toUIntOrNull() ?: 0U

private val onMode: UInt
get() = binding.onModeEd.text.toString().toUIntOrNull() ?: 0U

private val currentMode: Int
get() = binding.supportedModesSp.selectedItem.toString().split("-")[0].toInt()

private val binding
get() = _binding!!

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = ModeSelectFragmentBinding.inflate(inflater, container, false)
scope = viewLifecycleOwner.lifecycleScope

addressUpdateFragment =
childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment

binding.readAttributeBtn.setOnClickListener { scope.launch { readAttributeBtnClick() } }
binding.changeToModeBtn.setOnClickListener { scope.launch { changeToModeBtnClick() } }
binding.onModeWriteBtn.setOnClickListener {
scope.launch { writeAttributeBtnClick(ClusterIDMapping.ModeSelect.Attribute.OnMode, onMode) }
}
binding.startUpModeWriteBtn.setOnClickListener {
scope.launch {
writeAttributeBtnClick(ClusterIDMapping.ModeSelect.Attribute.StartUpMode, startUpMode)
}
}

return binding.root
}

private suspend fun readAttributeBtnClick() {
val endpointId = addressUpdateFragment.endpointId
val clusterId = ModeSelect.ID
val attributeId = ChipPathId.forWildcard().id
val path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId)
val devicePtr =
try {
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
} catch (e: IllegalStateException) {
Log.d(TAG, "getConnectedDevicePointer exception", e)
showMessage("Get DevicePointer fail!")
return
}
deviceController.readAttributePath(
object : ReportCallback {
override fun onError(
attributePath: ChipAttributePath?,
eventPath: ChipEventPath?,
e: Exception
) {
requireActivity().runOnUiThread {
Toast.makeText(
requireActivity(),
R.string.ota_provider_invalid_attribute,
Toast.LENGTH_SHORT
)
.show()
}
}

override fun onReport(nodeState: NodeState?) {
val attributeStates =
nodeState?.getEndpointState(endpointId)?.getClusterState(clusterId)?.attributeStates
?: return

requireActivity().runOnUiThread {
val description = attributeStates[ClusterIDMapping.ModeSelect.Attribute.Description.id]
binding.descriptionEd.setText(description?.value?.toString())

val standardNamespace =
attributeStates[ClusterIDMapping.ModeSelect.Attribute.StandardNamespace.id]
binding.standardNamespaceEd.setText(standardNamespace?.value?.toString())

val currentMode = attributeStates[ClusterIDMapping.ModeSelect.Attribute.CurrentMode.id]
binding.currentModeEd.setText(currentMode?.value?.toString())

setVisibility(
attributeStates[ClusterIDMapping.ModeSelect.Attribute.StartUpMode.id],
binding.startUpModeEd,
binding.startUpModeTv,
binding.startUpModeWriteBtn
)
setVisibility(
attributeStates[ClusterIDMapping.ModeSelect.Attribute.OnMode.id],
binding.onModeEd,
binding.onModeTv,
binding.onModeWriteBtn
)

val supportedModesTlv =
attributeStates[ClusterIDMapping.ModeSelect.Attribute.SupportedModes.id]?.tlv

supportedModesTlv?.let {
setSupportedModeSpinner(it, currentMode?.value?.toString()?.toUInt())
}
}
}
},
devicePtr,
listOf<ChipAttributePath>(path),
0
)
}

private fun setVisibility(
attribute: AttributeState?,
modeEd: EditText,
modeTv: TextView,
writeBtn: TextView
) {
val modeVisibility =
if (attribute != null) {
modeEd.setText(attribute.value?.toString() ?: "NULL")
View.VISIBLE
} else {
View.GONE
}
modeEd.visibility = modeVisibility
modeTv.visibility = modeVisibility
writeBtn.visibility = modeVisibility
}

private fun setSupportedModeSpinner(supportedModesTlv: ByteArray, currentModeValue: UInt?) {
var pos = 0
var currentItemId = 0
val modeOptionStructList: List<ModeSelectClusterModeOptionStruct>
TlvReader(supportedModesTlv).also {
modeOptionStructList = buildList {
it.enterArray(AnonymousTag)
while (!it.isEndOfContainer()) {
val struct = ModeSelectClusterModeOptionStruct.fromTlv(AnonymousTag, it)
add(struct)
if (currentModeValue != null && struct.mode == currentModeValue) {
currentItemId = pos
}
pos++
}
it.exitContainer()
}
binding.supportedModesSp.adapter =
ArrayAdapter(
requireContext(),
android.R.layout.simple_spinner_dropdown_item,
modeOptionStructList.map { it.show() }
)
binding.supportedModesSp.setSelection(currentItemId)
binding.currentModeEd.setText(binding.supportedModesSp.selectedItem.toString())
}
}

private suspend fun changeToModeBtnClick() {
val devicePtr =
try {
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
} catch (e: IllegalStateException) {
Log.d(TAG, "getConnectedDevicePointer exception", e)
showMessage("Get DevicePointer fail!")
return
}
ChipClusters.ModeSelectCluster(devicePtr, addressUpdateFragment.endpointId)
.changeToMode(
object : ChipClusters.DefaultClusterCallback {
override fun onError(error: java.lang.Exception?) {
Log.d(TAG, "onError", error)
showMessage("Error : ${error.toString()}")
}

override fun onSuccess() {
showMessage("Change Success")
scope.launch { readAttributeBtnClick() }
}
},
currentMode
)
}

private suspend fun writeAttributeBtnClick(attribute: ModeSelect.Attribute, value: UInt) {
val clusterId = ModeSelect.ID
val devicePtr =
try {
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
} catch (e: IllegalStateException) {
Log.d(TAG, "getConnectedDevicePointer exception", e)
showMessage("Get DevicePointer fail!")
return
}
deviceController.write(
object : WriteAttributesCallback {
override fun onError(attributePath: ChipAttributePath?, ex: java.lang.Exception?) {
showMessage("Write ${attribute.name} failure $ex")
Log.e(TAG, "Write ${attribute.name} failure", ex)
}

override fun onResponse(attributePath: ChipAttributePath, status: Status) {
showMessage("Write ${attribute.name} response: $status")
}
},
devicePtr,
listOf(
AttributeWriteRequest.newInstance(
addressUpdateFragment.endpointId,
clusterId,
attribute.id,
TlvWriter().put(AnonymousTag, value).getEncoded(),
Optional.empty()
)
),
0,
0
)
}

private fun ModeSelectClusterModeOptionStruct.show(): String {
val value = this
return StringBuilder()
.apply {
append("${value.mode}-${value.label}")
append("[")
for (semanticTag in value.semanticTags) {
append("${semanticTag.value}:${semanticTag.mfgCode}")
append(",")
}
append("]")
}
.toString()
}

override fun onDestroyView() {
super.onDestroyView()
deviceController.finishOTAProvider()
_binding = null
}

private fun showMessage(msg: String) {
requireActivity().runOnUiThread { binding.commandStatusTv.text = msg }
}

companion object {
private const val TAG = "ModeSelectClientFragment"

fun newInstance(): ModeSelectClientFragment = ModeSelectClientFragment()
}
}
Loading

0 comments on commit 3f62505

Please sign in to comment.