1
1
package com.google.chip.chiptool.clusterclient.clusterinteraction
2
2
3
3
import android.os.Bundle
4
+ import android.util.Log
4
5
import android.view.LayoutInflater
5
6
import android.view.View
6
7
import android.view.ViewGroup
8
+ import android.widget.ArrayAdapter
9
+ import android.widget.AutoCompleteTextView
10
+ import android.widget.LinearLayout
7
11
import android.widget.Toast
12
+ import androidx.constraintlayout.widget.ConstraintLayout
13
+ import androidx.core.view.forEach
8
14
import androidx.fragment.app.Fragment
9
- import androidx.lifecycle.lifecycleScope
15
+ import chip.clusterinfo.ClusterCommandCallback
16
+ import chip.clusterinfo.ClusterInfo
17
+ import chip.clusterinfo.CommandInfo
18
+ import chip.clusterinfo.CommandResponseInfo
19
+ import chip.clusterinfo.DelegatedClusterCallback
20
+ import chip.devicecontroller.ChipClusters
10
21
import chip.devicecontroller.ChipDeviceController
22
+ import chip.devicecontroller.ClusterInfoMapping
11
23
import com.google.chip.chiptool.ChipClient
12
24
import com.google.chip.chiptool.GenericChipDeviceListener
13
25
import com.google.chip.chiptool.R
26
+ import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackDataTv
27
+ import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackNameTv
28
+ import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackTypeTv
29
+ import kotlinx.android.synthetic.main.cluster_detail_fragment.view.callbackList
30
+ import kotlinx.android.synthetic.main.cluster_detail_fragment.view.clusterAutoCompleteTv
31
+ import kotlinx.android.synthetic.main.cluster_detail_fragment.view.commandAutoCompleteTv
32
+ import kotlinx.android.synthetic.main.cluster_detail_fragment.view.invokeCommand
33
+ import kotlinx.android.synthetic.main.cluster_detail_fragment.view.parameterList
34
+ import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParameterData
35
+ import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParameterNameTv
36
+ import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParameterTypeTv
14
37
import kotlinx.coroutines.CoroutineScope
38
+ import kotlinx.coroutines.Dispatchers
39
+ import kotlinx.coroutines.Job
40
+ import kotlinx.coroutines.cancel
15
41
16
42
/* *
17
43
* ClusterDetailFragment allows user to pick cluster, command, specify parameters and see
18
44
* the callback result.
19
45
*/
20
- class ClusterDetailFragment : Fragment (){
46
+ class ClusterDetailFragment : Fragment () {
21
47
private val deviceController: ChipDeviceController
22
48
get() = ChipClient .getDeviceController(requireContext())
23
49
24
- private lateinit var scope: CoroutineScope
50
+ private val scope = CoroutineScope (Dispatchers .Main + Job ())
51
+ private lateinit var clusterMap: Map <String , ClusterInfo >
52
+ private lateinit var selectedClusterInfo: ClusterInfo
53
+ private lateinit var selectedCluster: ChipClusters .BaseChipCluster
54
+ private lateinit var selectedCommandCallback: DelegatedClusterCallback
55
+ private lateinit var selectedCommandInfo: CommandInfo
56
+ private var devicePtr = 0L
57
+ private var endpointId = 0
25
58
26
59
override fun onCreateView (
27
60
inflater : LayoutInflater ,
28
61
container : ViewGroup ? ,
29
62
savedInstanceState : Bundle ?
30
63
): View {
31
- scope = viewLifecycleOwner.lifecycleScope
32
-
64
+ clusterMap = ClusterInfoMapping ().clusterMap
65
+ devicePtr = checkNotNull(requireArguments().getLong(DEVICE_PTR_KEY ))
66
+ endpointId = checkNotNull(requireArguments().getInt(ENDPOINT_ID_KEY ))
33
67
return inflater.inflate(R .layout.cluster_detail_fragment, container, false ).apply {
34
68
deviceController.setCompletionListener(GenericChipDeviceListener ())
69
+ commandAutoCompleteTv.visibility = View .GONE
70
+ clusterAutoCompleteSetup(clusterAutoCompleteTv, commandAutoCompleteTv, parameterList)
71
+ commandAutoCompleteSetup(commandAutoCompleteTv, inflater, parameterList, callbackList)
72
+ invokeCommand.setOnClickListener {
73
+ val commandArguments = HashMap <String , Any >()
74
+ parameterList.forEach {
75
+ val type =
76
+ selectedCommandInfo.commandParameters[it.clusterParameterNameTv.text.toString()]!! .type
77
+ val data = castStringToType(it.clusterParameterData.text.toString(), type)!!
78
+
79
+ commandArguments[it.clusterParameterNameTv.text.toString()] = data
80
+ }
81
+ selectedCommandInfo.getCommandFunction()
82
+ .invokeCommand(selectedCluster, selectedCommandCallback, commandArguments)
83
+ }
84
+ }
85
+ }
86
+
87
+ private fun castStringToType (data : String , type : Class <* >): Any? {
88
+ return when (type) {
89
+ Int ::class .java -> data.toInt()
90
+ String ::class .java -> data
91
+ Boolean ::class .java -> data.toBoolean()
92
+ else -> null
35
93
}
36
94
}
37
95
@@ -41,8 +99,119 @@ class ClusterDetailFragment : Fragment(){
41
99
}
42
100
}
43
101
102
+ private fun clusterAutoCompleteSetup (
103
+ clusterAutoComplete : AutoCompleteTextView ,
104
+ commandAutoComplete : AutoCompleteTextView ,
105
+ parameterList : LinearLayout
106
+ ) {
107
+ val clusterNameList = constructHint(clusterMap)
108
+ val clusterAdapter =
109
+ ArrayAdapter (requireContext(), android.R .layout.simple_list_item_1, clusterNameList)
110
+ clusterAutoComplete.setAdapter(clusterAdapter)
111
+ clusterAutoComplete.setOnItemClickListener { parent, view, position, id ->
112
+ commandAutoComplete.visibility = View .VISIBLE
113
+ // when new cluster is selected, clear the command text and possible parameterList
114
+ commandAutoComplete.setText(" " , false )
115
+ parameterList.removeAllViews()
116
+ // populate all the commands that belong to the selected cluster
117
+ val selectedCluster: String = clusterAutoComplete.adapter.getItem(position).toString()
118
+ val commandAdapter = getCommandOptions(selectedCluster)
119
+ commandAutoComplete.setAdapter(commandAdapter)
120
+ }
121
+ }
122
+
123
+ private fun commandAutoCompleteSetup (
124
+ commandAutoComplete : AutoCompleteTextView ,
125
+ inflater : LayoutInflater ,
126
+ parameterList : LinearLayout ,
127
+ callbackList : LinearLayout
128
+ ) {
129
+ commandAutoComplete.setOnItemClickListener { parent, view, position, id ->
130
+ // when new command is selected, clear all the parameterList
131
+ parameterList.removeAllViews()
132
+ selectedCluster = selectedClusterInfo.createClusterFunction.create(devicePtr, endpointId)
133
+ val selectedCommand: String = commandAutoComplete.adapter.getItem(position).toString()
134
+ selectedCommandInfo = selectedClusterInfo.commands[selectedCommand]!!
135
+ selectedCommandCallback = selectedCommandInfo.commandCallbackSupplier.get()
136
+ populateCommandParameter(inflater, parameterList)
137
+ selectedCommandCallback.setCallbackDelegate(object : ClusterCommandCallback {
138
+ override fun onSuccess (responseValues : Map <CommandResponseInfo , Any >) {
139
+ showMessage(" Command success" )
140
+ // Populate UI based on response values. We know the types from CommandInfo.getCommandResponses().
141
+ requireActivity().runOnUiThread {
142
+ populateCallbackResult(
143
+ responseValues,
144
+ inflater,
145
+ callbackList
146
+ )
147
+ }
148
+ responseValues.forEach { Log .d(TAG , it.toString()) }
149
+ }
150
+
151
+ override fun onFailure (exception : Exception ) {
152
+ showMessage(" Command failed" )
153
+ Log .e(TAG , exception.toString())
154
+ }
155
+ })
156
+ }
157
+ }
158
+
159
+ private fun populateCommandParameter (inflater : LayoutInflater , parameterList : LinearLayout ) {
160
+ selectedCommandInfo.commandParameters.forEach { (paramName, paramInfo) ->
161
+ val param = inflater.inflate(R .layout.cluster_parameter_item, null , false ) as ConstraintLayout
162
+ param.clusterParameterNameTv.text = " ${paramName} "
163
+ param.clusterParameterTypeTv.text = " ${paramInfo.type} "
164
+ parameterList.addView(param)
165
+ }
166
+ }
167
+
168
+ private fun populateCallbackResult (
169
+ responseValues : Map <CommandResponseInfo , Any >,
170
+ inflater : LayoutInflater ,
171
+ callbackList : LinearLayout
172
+ ) {
173
+ responseValues.forEach { (variableNameType, response) ->
174
+ val callback =
175
+ inflater.inflate(R .layout.cluster_callback_item, null , false ) as ConstraintLayout
176
+ callback.clusterCallbackNameTv.text = variableNameType.name
177
+ callback.clusterCallbackDataTv.text = response.toString()
178
+ callback.clusterCallbackTypeTv.text = variableNameType.type
179
+ callbackList.addView(callback)
180
+ }
181
+ }
182
+
183
+ private fun getCommandOptions (
184
+ clusterName : String
185
+ ): ArrayAdapter <String > {
186
+ selectedClusterInfo = clusterMap[clusterName]!!
187
+ val commandNameList = constructHint(selectedClusterInfo.commands)
188
+ return ArrayAdapter (requireContext(), android.R .layout.simple_list_item_1, commandNameList)
189
+ }
190
+
191
+ private fun constructHint (clusterMap : Map <String , * >): Array <String > {
192
+ return clusterMap.keys.toTypedArray()
193
+ }
194
+
195
+ override fun onStop () {
196
+ super .onStop()
197
+ scope.cancel()
198
+ }
199
+
44
200
companion object {
45
201
private const val TAG = " ClusterDetailFragment"
46
- fun newInstance (): ClusterDetailFragment = ClusterDetailFragment ()
202
+ private const val ENDPOINT_ID_KEY = " endpoint_id"
203
+ private const val DEVICE_PTR_KEY = " device_ptr"
204
+
205
+ fun newInstance (
206
+ deviceId : Long ,
207
+ endpointId : Int
208
+ ): ClusterDetailFragment {
209
+ return ClusterDetailFragment ().apply {
210
+ arguments = Bundle (2 ).apply {
211
+ putLong(DEVICE_PTR_KEY , deviceId)
212
+ putInt(ENDPOINT_ID_KEY , endpointId)
213
+ }
214
+ }
215
+ }
47
216
}
48
217
}
0 commit comments