@@ -2,25 +2,37 @@ package com.google.chip.chiptool.clusterclient
2
2
3
3
import android.content.Context
4
4
import android.os.Bundle
5
+ import android.os.Handler
6
+ import android.os.Looper
5
7
import android.util.Log
6
8
import android.view.LayoutInflater
7
9
import android.view.View
8
10
import android.view.ViewGroup
9
11
import android.widget.AdapterView
10
12
import android.widget.ArrayAdapter
13
+ import android.widget.Toast
11
14
import androidx.fragment.app.Fragment
15
+ import androidx.lifecycle.lifecycleScope
16
+ import chip.devicecontroller.ChipClusterException
17
+ import chip.devicecontroller.ChipClusters
12
18
import chip.devicecontroller.ChipDeviceController
19
+ import chip.devicecontroller.ICDClientInfo
13
20
import com.google.chip.chiptool.ChipClient
14
21
import com.google.chip.chiptool.databinding.AddressUpdateFragmentBinding
15
22
import com.google.chip.chiptool.util.DeviceIdUtil
23
+ import kotlin.coroutines.resume
24
+ import kotlin.coroutines.resumeWithException
25
+ import kotlin.coroutines.suspendCoroutine
26
+ import kotlinx.coroutines.CoroutineScope
27
+ import kotlinx.coroutines.launch
16
28
17
29
/* * Fragment for updating the address of a device given its fabric and node ID. */
18
- class AddressUpdateFragment : Fragment () {
30
+ class AddressUpdateFragment : ICDCheckInCallback , Fragment () {
19
31
private val deviceController: ChipDeviceController
20
32
get() = ChipClient .getDeviceController(requireContext())
21
33
22
34
val deviceId: Long
23
- get() = binding.deviceIdEd.text.toString().toULong().toLong()
35
+ get() = binding.deviceIdEd.text.toString().toULong(16 ).toLong()
24
36
25
37
var endpointId: Int
26
38
get() = binding.epIdEd.text.toString().toInt()
@@ -32,22 +44,44 @@ class AddressUpdateFragment : Fragment() {
32
44
private val binding
33
45
get() = _binding !!
34
46
47
+ private lateinit var scope: CoroutineScope
48
+
49
+ private var icdDeviceId: Long = 0L
50
+ private var icdTotalRemainStayActiveTimeMs = 0L
51
+ private var icdDeviceRemainStayActiveTimeMs = 0L
52
+ private var isSendingStayActiveCommand = false
53
+ private val icdRequestActiveDurationMs: Long
54
+ get() = binding.icdActiveDurationEd.text.toString().toLong() * 1000
55
+
56
+ private val handler = Handler (Looper .getMainLooper())
57
+
35
58
override fun onCreateView (
36
59
inflater : LayoutInflater ,
37
60
container : ViewGroup ? ,
38
61
savedInstanceState : Bundle ?
39
62
): View {
40
63
_binding = AddressUpdateFragmentBinding .inflate(inflater, container, false )
64
+ scope = viewLifecycleOwner.lifecycleScope
41
65
return binding.root
42
66
}
43
67
44
68
override fun onViewCreated (view : View , savedInstanceState : Bundle ? ) {
45
69
super .onViewCreated(view, savedInstanceState)
46
70
71
+ ChipClient .setICDCheckInCallback(this )
72
+
47
73
val compressedFabricId = deviceController.compressedFabricId
48
- binding.fabricIdEd.setText(compressedFabricId.toULong().toString().padStart(16 , ' 0' ))
74
+ binding.fabricIdEd.setText(compressedFabricId.toULong().toString(16 ).padStart(16 , ' 0' ))
49
75
binding.deviceIdEd.setText(DeviceIdUtil .getLastDeviceId(requireContext()).toString(16 ))
50
76
binding.epIdEd.setText(endpointId.toString())
77
+ binding.icdActiveDurationEd.setText((ICD_STAY_ACTIVE_DURATION / 1000 ).toString())
78
+
79
+ binding.icdInteractionSwitch.setOnClickListener {
80
+ val isChecked = binding.icdInteractionSwitch.isChecked
81
+ if (updateUIForICDInteractionSwitch(isChecked)) {
82
+ icdInteractionSwitchClick(isChecked)
83
+ }
84
+ }
51
85
52
86
updateDeviceIdSpinner()
53
87
}
@@ -68,6 +102,61 @@ class AddressUpdateFragment : Fragment() {
68
102
}
69
103
}
70
104
105
+ private fun icdInteractionSwitchClick (isEnabled : Boolean ) {
106
+ if (isEnabled) {
107
+ icdDeviceId = deviceId
108
+ } else {
109
+ icdDeviceId = 0
110
+ if (icdDeviceRemainStayActiveTimeMs != 0L || icdTotalRemainStayActiveTimeMs != 0L ) {
111
+ scope.launch {
112
+ icdDeviceRemainStayActiveTimeMs = sendStayActive(0L )
113
+ icdTotalRemainStayActiveTimeMs = icdDeviceRemainStayActiveTimeMs
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ private suspend fun sendStayActive (duration : Long ): Long {
120
+ isSendingStayActiveCommand = true
121
+ val devicePtr =
122
+ try {
123
+ ChipClient .getConnectedDevicePointer(requireContext(), deviceId)
124
+ } catch (e: IllegalStateException ) {
125
+ Log .d(TAG , " getConnectedDevicePointer exception" , e)
126
+ showToastMessage(" Get DevicePointer fail!" )
127
+ throw e
128
+ }
129
+
130
+ val cluster = ChipClusters .IcdManagementCluster (devicePtr, 0 )
131
+ val duration = suspendCoroutine { cont ->
132
+ cluster.stayActiveRequest(
133
+ object : ChipClusters .IcdManagementCluster .StayActiveResponseCallback {
134
+ override fun onError (error : Exception ) {
135
+ cont.resumeWithException(error)
136
+ }
137
+
138
+ override fun onSuccess (promisedActiveDuration : Long ) {
139
+ cont.resume(promisedActiveDuration)
140
+ }
141
+ },
142
+ duration
143
+ )
144
+ }
145
+ isSendingStayActiveCommand = false
146
+ return duration
147
+ }
148
+
149
+ private fun updateUIForICDInteractionSwitch (isEnabled : Boolean ): Boolean {
150
+ val isICD =
151
+ deviceController.icdClientInfo.firstOrNull { info -> info.peerNodeId == deviceId } != null
152
+ if (isEnabled && ! isICD) {
153
+ binding.icdInteractionSwitch.isChecked = false
154
+ return false
155
+ }
156
+
157
+ return true
158
+ }
159
+
71
160
override fun onDestroyView () {
72
161
super .onDestroyView()
73
162
_binding = null
@@ -81,6 +170,12 @@ class AddressUpdateFragment : Fragment() {
81
170
}
82
171
}
83
172
173
+ private fun showToastMessage (msg : String ) {
174
+ requireActivity().runOnUiThread {
175
+ Toast .makeText(requireActivity(), msg, Toast .LENGTH_SHORT ).show()
176
+ }
177
+ }
178
+
84
179
fun isGroupId (): Boolean {
85
180
return isGroupNodeId(getNodeId())
86
181
}
@@ -90,7 +185,58 @@ class AddressUpdateFragment : Fragment() {
90
185
}
91
186
92
187
fun getNodeId (): ULong {
93
- return binding.deviceIdEd.text.toString().toULong()
188
+ return binding.deviceIdEd.text.toString().toULong(16 )
189
+ }
190
+
191
+ override fun notifyCheckInMessage (info : ICDClientInfo ) {
192
+ if (info.peerNodeId != icdDeviceId) {
193
+ return
194
+ }
195
+
196
+ scope.launch {
197
+ try {
198
+ icdDeviceRemainStayActiveTimeMs = sendStayActive(icdRequestActiveDurationMs)
199
+ icdTotalRemainStayActiveTimeMs = icdRequestActiveDurationMs
200
+ turnOnActiveMode()
201
+ } catch (e: IllegalStateException ) {
202
+ Log .d(TAG , " IlligalStateException" , e)
203
+ } catch (e: ChipClusterException ) {
204
+ Log .d(TAG , " ChipClusterException" , e)
205
+ }
206
+ }
207
+ }
208
+
209
+ private fun turnOnActiveMode () {
210
+ requireActivity().runOnUiThread {
211
+ binding.icdProgressBar.max = (icdTotalRemainStayActiveTimeMs / 1000 ).toInt()
212
+ binding.icdProgressBar.progress = (icdTotalRemainStayActiveTimeMs / 1000 ).toInt()
213
+ }
214
+
215
+ val runnable =
216
+ object : Runnable {
217
+ override fun run () {
218
+ if (icdTotalRemainStayActiveTimeMs >= ICD_PROGRESS_STEP ) {
219
+ icdDeviceRemainStayActiveTimeMs - = ICD_PROGRESS_STEP
220
+ icdTotalRemainStayActiveTimeMs - = ICD_PROGRESS_STEP
221
+ handler.postDelayed(this , ICD_PROGRESS_STEP )
222
+ requireActivity().runOnUiThread {
223
+ binding.icdProgressBar.progress = (icdTotalRemainStayActiveTimeMs / 1000 ).toInt()
224
+ }
225
+
226
+ if (
227
+ ! isSendingStayActiveCommand &&
228
+ (ICD_RESEND_STAY_ACTIVE_TIME > icdDeviceRemainStayActiveTimeMs) &&
229
+ (ICD_RESEND_STAY_ACTIVE_TIME < icdTotalRemainStayActiveTimeMs)
230
+ )
231
+ scope.launch {
232
+ icdDeviceRemainStayActiveTimeMs = sendStayActive(icdTotalRemainStayActiveTimeMs)
233
+ }
234
+ } else {
235
+ requireActivity().runOnUiThread { binding.icdProgressBar.progress = 0 }
236
+ }
237
+ }
238
+ }
239
+ handler.post(runnable)
94
240
}
95
241
96
242
companion object {
@@ -99,6 +245,10 @@ class AddressUpdateFragment : Fragment() {
99
245
private const val MIN_GROUP_NODE_ID = 0xFFFF_FFFF_FFFF_0000UL
100
246
private const val MASK_GROUP_ID = 0x0000_0000_0000_FFFFUL
101
247
248
+ private const val ICD_STAY_ACTIVE_DURATION = 30000L // 30 secs.
249
+ private const val ICD_PROGRESS_STEP = 1000L // 1 sec.
250
+ private const val ICD_RESEND_STAY_ACTIVE_TIME = 2000L // 2 secs.
251
+
102
252
fun isGroupNodeId (nodeId : ULong ): Boolean {
103
253
return nodeId >= MIN_GROUP_NODE_ID
104
254
}
@@ -112,3 +262,7 @@ class AddressUpdateFragment : Fragment() {
112
262
}
113
263
}
114
264
}
265
+
266
+ interface ICDCheckInCallback {
267
+ fun notifyCheckInMessage (info : ICDClientInfo )
268
+ }
0 commit comments