Skip to content

Commit 3f62505

Browse files
authoredJan 10, 2025
[Android] Implement Mode Select fragment in Chiptool (#36999)
* Implement Mode Select fragment * Fix refactoring
1 parent 522c555 commit 3f62505

File tree

5 files changed

+540
-0
lines changed

5 files changed

+540
-0
lines changed
 

‎examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt

+5
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class SelectActionFragment : Fragment() {
7676
binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() }
7777
binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() }
7878
binding.icdBtn.setOnClickListener { handleICDClicked() }
79+
binding.modeSelectBtn.setOnClickListener { handleModeSelectClicked() }
7980

8081
return binding.root
8182
}
@@ -255,6 +256,10 @@ class SelectActionFragment : Fragment() {
255256
showFragment(ICDFragment.newInstance())
256257
}
257258

259+
private fun handleModeSelectClicked() {
260+
showFragment(ModeSelectClientFragment.newInstance())
261+
}
262+
258263
companion object {
259264

260265
@JvmStatic fun newInstance() = SelectActionFragment()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
package com.google.chip.chiptool.clusterclient
2+
3+
import android.os.Bundle
4+
import android.util.Log
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import android.widget.ArrayAdapter
9+
import android.widget.EditText
10+
import android.widget.TextView
11+
import android.widget.Toast
12+
import androidx.fragment.app.Fragment
13+
import androidx.lifecycle.lifecycleScope
14+
import chip.devicecontroller.ChipClusters
15+
import chip.devicecontroller.ChipDeviceController
16+
import chip.devicecontroller.ClusterIDMapping
17+
import chip.devicecontroller.ClusterIDMapping.ModeSelect
18+
import chip.devicecontroller.ReportCallback
19+
import chip.devicecontroller.WriteAttributesCallback
20+
import chip.devicecontroller.cluster.structs.ModeSelectClusterModeOptionStruct
21+
import chip.devicecontroller.model.AttributeState
22+
import chip.devicecontroller.model.AttributeWriteRequest
23+
import chip.devicecontroller.model.ChipAttributePath
24+
import chip.devicecontroller.model.ChipEventPath
25+
import chip.devicecontroller.model.ChipPathId
26+
import chip.devicecontroller.model.NodeState
27+
import chip.devicecontroller.model.Status
28+
import com.google.chip.chiptool.ChipClient
29+
import com.google.chip.chiptool.R
30+
import com.google.chip.chiptool.databinding.ModeSelectFragmentBinding
31+
import java.util.Optional
32+
import kotlinx.coroutines.CoroutineScope
33+
import kotlinx.coroutines.launch
34+
import matter.tlv.AnonymousTag
35+
import matter.tlv.TlvReader
36+
import matter.tlv.TlvWriter
37+
38+
class ModeSelectClientFragment : Fragment() {
39+
private val deviceController: ChipDeviceController
40+
get() = ChipClient.getDeviceController(requireContext())
41+
42+
private lateinit var scope: CoroutineScope
43+
44+
private lateinit var addressUpdateFragment: AddressUpdateFragment
45+
46+
private var _binding: ModeSelectFragmentBinding? = null
47+
48+
private val startUpMode: UInt
49+
get() = binding.startUpModeEd.text.toString().toUIntOrNull() ?: 0U
50+
51+
private val onMode: UInt
52+
get() = binding.onModeEd.text.toString().toUIntOrNull() ?: 0U
53+
54+
private val currentMode: Int
55+
get() = binding.supportedModesSp.selectedItem.toString().split("-")[0].toInt()
56+
57+
private val binding
58+
get() = _binding!!
59+
60+
override fun onCreateView(
61+
inflater: LayoutInflater,
62+
container: ViewGroup?,
63+
savedInstanceState: Bundle?
64+
): View {
65+
_binding = ModeSelectFragmentBinding.inflate(inflater, container, false)
66+
scope = viewLifecycleOwner.lifecycleScope
67+
68+
addressUpdateFragment =
69+
childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment
70+
71+
binding.readAttributeBtn.setOnClickListener { scope.launch { readAttributeBtnClick() } }
72+
binding.changeToModeBtn.setOnClickListener { scope.launch { changeToModeBtnClick() } }
73+
binding.onModeWriteBtn.setOnClickListener {
74+
scope.launch { writeAttributeBtnClick(ClusterIDMapping.ModeSelect.Attribute.OnMode, onMode) }
75+
}
76+
binding.startUpModeWriteBtn.setOnClickListener {
77+
scope.launch {
78+
writeAttributeBtnClick(ClusterIDMapping.ModeSelect.Attribute.StartUpMode, startUpMode)
79+
}
80+
}
81+
82+
return binding.root
83+
}
84+
85+
private suspend fun readAttributeBtnClick() {
86+
val endpointId = addressUpdateFragment.endpointId
87+
val clusterId = ModeSelect.ID
88+
val attributeId = ChipPathId.forWildcard().id
89+
val path = ChipAttributePath.newInstance(endpointId, clusterId, attributeId)
90+
val devicePtr =
91+
try {
92+
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
93+
} catch (e: IllegalStateException) {
94+
Log.d(TAG, "getConnectedDevicePointer exception", e)
95+
showMessage("Get DevicePointer fail!")
96+
return
97+
}
98+
deviceController.readAttributePath(
99+
object : ReportCallback {
100+
override fun onError(
101+
attributePath: ChipAttributePath?,
102+
eventPath: ChipEventPath?,
103+
e: Exception
104+
) {
105+
requireActivity().runOnUiThread {
106+
Toast.makeText(
107+
requireActivity(),
108+
R.string.ota_provider_invalid_attribute,
109+
Toast.LENGTH_SHORT
110+
)
111+
.show()
112+
}
113+
}
114+
115+
override fun onReport(nodeState: NodeState?) {
116+
val attributeStates =
117+
nodeState?.getEndpointState(endpointId)?.getClusterState(clusterId)?.attributeStates
118+
?: return
119+
120+
requireActivity().runOnUiThread {
121+
val description = attributeStates[ClusterIDMapping.ModeSelect.Attribute.Description.id]
122+
binding.descriptionEd.setText(description?.value?.toString())
123+
124+
val standardNamespace =
125+
attributeStates[ClusterIDMapping.ModeSelect.Attribute.StandardNamespace.id]
126+
binding.standardNamespaceEd.setText(standardNamespace?.value?.toString())
127+
128+
val currentMode = attributeStates[ClusterIDMapping.ModeSelect.Attribute.CurrentMode.id]
129+
binding.currentModeEd.setText(currentMode?.value?.toString())
130+
131+
setVisibility(
132+
attributeStates[ClusterIDMapping.ModeSelect.Attribute.StartUpMode.id],
133+
binding.startUpModeEd,
134+
binding.startUpModeTv,
135+
binding.startUpModeWriteBtn
136+
)
137+
setVisibility(
138+
attributeStates[ClusterIDMapping.ModeSelect.Attribute.OnMode.id],
139+
binding.onModeEd,
140+
binding.onModeTv,
141+
binding.onModeWriteBtn
142+
)
143+
144+
val supportedModesTlv =
145+
attributeStates[ClusterIDMapping.ModeSelect.Attribute.SupportedModes.id]?.tlv
146+
147+
supportedModesTlv?.let {
148+
setSupportedModeSpinner(it, currentMode?.value?.toString()?.toUInt())
149+
}
150+
}
151+
}
152+
},
153+
devicePtr,
154+
listOf<ChipAttributePath>(path),
155+
0
156+
)
157+
}
158+
159+
private fun setVisibility(
160+
attribute: AttributeState?,
161+
modeEd: EditText,
162+
modeTv: TextView,
163+
writeBtn: TextView
164+
) {
165+
val modeVisibility =
166+
if (attribute != null) {
167+
modeEd.setText(attribute.value?.toString() ?: "NULL")
168+
View.VISIBLE
169+
} else {
170+
View.GONE
171+
}
172+
modeEd.visibility = modeVisibility
173+
modeTv.visibility = modeVisibility
174+
writeBtn.visibility = modeVisibility
175+
}
176+
177+
private fun setSupportedModeSpinner(supportedModesTlv: ByteArray, currentModeValue: UInt?) {
178+
var pos = 0
179+
var currentItemId = 0
180+
val modeOptionStructList: List<ModeSelectClusterModeOptionStruct>
181+
TlvReader(supportedModesTlv).also {
182+
modeOptionStructList = buildList {
183+
it.enterArray(AnonymousTag)
184+
while (!it.isEndOfContainer()) {
185+
val struct = ModeSelectClusterModeOptionStruct.fromTlv(AnonymousTag, it)
186+
add(struct)
187+
if (currentModeValue != null && struct.mode == currentModeValue) {
188+
currentItemId = pos
189+
}
190+
pos++
191+
}
192+
it.exitContainer()
193+
}
194+
binding.supportedModesSp.adapter =
195+
ArrayAdapter(
196+
requireContext(),
197+
android.R.layout.simple_spinner_dropdown_item,
198+
modeOptionStructList.map { it.show() }
199+
)
200+
binding.supportedModesSp.setSelection(currentItemId)
201+
binding.currentModeEd.setText(binding.supportedModesSp.selectedItem.toString())
202+
}
203+
}
204+
205+
private suspend fun changeToModeBtnClick() {
206+
val devicePtr =
207+
try {
208+
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
209+
} catch (e: IllegalStateException) {
210+
Log.d(TAG, "getConnectedDevicePointer exception", e)
211+
showMessage("Get DevicePointer fail!")
212+
return
213+
}
214+
ChipClusters.ModeSelectCluster(devicePtr, addressUpdateFragment.endpointId)
215+
.changeToMode(
216+
object : ChipClusters.DefaultClusterCallback {
217+
override fun onError(error: java.lang.Exception?) {
218+
Log.d(TAG, "onError", error)
219+
showMessage("Error : ${error.toString()}")
220+
}
221+
222+
override fun onSuccess() {
223+
showMessage("Change Success")
224+
scope.launch { readAttributeBtnClick() }
225+
}
226+
},
227+
currentMode
228+
)
229+
}
230+
231+
private suspend fun writeAttributeBtnClick(attribute: ModeSelect.Attribute, value: UInt) {
232+
val clusterId = ModeSelect.ID
233+
val devicePtr =
234+
try {
235+
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
236+
} catch (e: IllegalStateException) {
237+
Log.d(TAG, "getConnectedDevicePointer exception", e)
238+
showMessage("Get DevicePointer fail!")
239+
return
240+
}
241+
deviceController.write(
242+
object : WriteAttributesCallback {
243+
override fun onError(attributePath: ChipAttributePath?, ex: java.lang.Exception?) {
244+
showMessage("Write ${attribute.name} failure $ex")
245+
Log.e(TAG, "Write ${attribute.name} failure", ex)
246+
}
247+
248+
override fun onResponse(attributePath: ChipAttributePath, status: Status) {
249+
showMessage("Write ${attribute.name} response: $status")
250+
}
251+
},
252+
devicePtr,
253+
listOf(
254+
AttributeWriteRequest.newInstance(
255+
addressUpdateFragment.endpointId,
256+
clusterId,
257+
attribute.id,
258+
TlvWriter().put(AnonymousTag, value).getEncoded(),
259+
Optional.empty()
260+
)
261+
),
262+
0,
263+
0
264+
)
265+
}
266+
267+
private fun ModeSelectClusterModeOptionStruct.show(): String {
268+
val value = this
269+
return StringBuilder()
270+
.apply {
271+
append("${value.mode}-${value.label}")
272+
append("[")
273+
for (semanticTag in value.semanticTags) {
274+
append("${semanticTag.value}:${semanticTag.mfgCode}")
275+
append(",")
276+
}
277+
append("]")
278+
}
279+
.toString()
280+
}
281+
282+
override fun onDestroyView() {
283+
super.onDestroyView()
284+
deviceController.finishOTAProvider()
285+
_binding = null
286+
}
287+
288+
private fun showMessage(msg: String) {
289+
requireActivity().runOnUiThread { binding.commandStatusTv.text = msg }
290+
}
291+
292+
companion object {
293+
private const val TAG = "ModeSelectClientFragment"
294+
295+
fun newInstance(): ModeSelectClientFragment = ModeSelectClientFragment()
296+
}
297+
}

0 commit comments

Comments
 (0)
Please sign in to comment.