From c339dc99739a5b127e4500e18ddf69655e950511 Mon Sep 17 00:00:00 2001 From: joonhaengHeo <85541460+joonhaengHeo@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:10:30 +0900 Subject: [PATCH] [Android] Implement multiple write invoke UI (#32352) * Implement multiple write,invoke UI * fix reset btn --- .../clusterclient/WildcardFragment.kt | 240 +++++++++++------- .../app/src/main/res/layout/invoke_dialog.xml | 28 +- .../src/main/res/layout/wildcard_fragment.xml | 104 +++++++- .../app/src/main/res/layout/write_dialog.xml | 55 +--- .../app/src/main/res/values/strings.xml | 14 +- 5 files changed, 264 insertions(+), 177 deletions(-) diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt index 5b49a6d9ab05ab..2ffbec50c746a2 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt @@ -16,7 +16,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import chip.devicecontroller.ChipDeviceController import chip.devicecontroller.ChipIdLookup -import chip.devicecontroller.InvokeCallback +import chip.devicecontroller.ExtendableInvokeCallback import chip.devicecontroller.ReportCallback import chip.devicecontroller.ResubscriptionAttemptCallback import chip.devicecontroller.SubscriptionEstablishedCallback @@ -26,6 +26,8 @@ import chip.devicecontroller.model.ChipAttributePath import chip.devicecontroller.model.ChipEventPath import chip.devicecontroller.model.ChipPathId import chip.devicecontroller.model.InvokeElement +import chip.devicecontroller.model.InvokeResponseData +import chip.devicecontroller.model.NoInvokeResponseData import chip.devicecontroller.model.NodeState import chip.devicecontroller.model.Status import com.google.chip.chiptool.ChipClient @@ -57,6 +59,8 @@ class WildcardFragment : Fragment() { private val attributePath = ArrayList() private val eventPath = ArrayList() + private val writePath = ArrayList() + private val invokePath = ArrayList() private val subscribeIdList = ArrayList() private val reportCallback = @@ -89,29 +93,47 @@ class WildcardFragment : Fragment() { private val writeAttributeCallback = object : WriteAttributesCallback { + var viewText = "" + override fun onError(attributePath: ChipAttributePath?, ex: Exception?) { Log.e(TAG, "Report error for $attributePath: $ex") + viewText += "onError : $attributePath - $ex\n" } override fun onResponse(attributePath: ChipAttributePath, status: Status) { - val text = "$attributePath : Write response: $status" - requireActivity().runOnUiThread { binding.outputTv.text = text } + viewText += "$attributePath : Write response: $status\n" } override fun onDone() { - Log.i(TAG, "write attribute Done") + requireActivity().runOnUiThread { + binding.outputTv.text = viewText + viewText = "" + } } } - private val invokeCallback = - object : InvokeCallback { + private val extendableInvokeCallback = + object : ExtendableInvokeCallback { + var viewText = "" + override fun onError(e: java.lang.Exception?) { + viewText += "Invoke onError : $e\n" Log.e(TAG, "Report error", e) } - override fun onResponse(invokeElement: InvokeElement?, successCode: Long) { - val text = "Invoke Response : $invokeElement, $successCode" - requireActivity().runOnUiThread { binding.outputTv.text = text } + override fun onResponse(invokeResponseData: InvokeResponseData?) { + viewText += "Invoke Response : $invokeResponseData\n" + } + + override fun onNoResponse(noInvokeResponseData: NoInvokeResponseData?) { + viewText += "Invoke onNoResponse : $noInvokeResponseData\n" + } + + override fun onDone() { + requireActivity().runOnUiThread { + binding.outputTv.text = viewText + viewText = "" + } } } @@ -123,41 +145,27 @@ class WildcardFragment : Fragment() { _binding = WildcardFragmentBinding.inflate(inflater, container, false) scope = viewLifecycleOwner.lifecycleScope - binding.selectTypeRadioGroup.setOnCheckedChangeListener { _, i -> - val readBtnOn = (i == R.id.readRadioBtn) - val subscribeBtnOn = (i == R.id.subscribeRadioBtn) - val writeBtnOn = (i == R.id.writeRadioBtn) - val invokeBtnOn = (i == R.id.invokeRadioBtn) - - binding.addLayout.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.attributeIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) - binding.attributeIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) - binding.eventIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.eventIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn) - binding.commandIdLabel.visibility = getVisibility(invokeBtnOn) - binding.commandIdEd.visibility = getVisibility(invokeBtnOn) - binding.isUrgentLabel.visibility = getVisibility(subscribeBtnOn) - binding.isUrgentSp.visibility = getVisibility(subscribeBtnOn) - binding.shutdownSubscriptionBtn.visibility = getVisibility(subscribeBtnOn) - } + val writeTypeSpinnerAdapter = + ArrayAdapter( + requireActivity(), + android.R.layout.simple_spinner_dropdown_item, + TLV_MAP.keys.toList() + ) + binding.writeValueTypeSp.adapter = writeTypeSpinnerAdapter - binding.sendBtn.setOnClickListener { - if (binding.readRadioBtn.isChecked) { - showReadDialog() - } else if (binding.subscribeRadioBtn.isChecked) { - showSubscribeDialog() - } else if (binding.writeRadioBtn.isChecked) { - showWriteDialog() - } else if (binding.invokeRadioBtn.isChecked) { - showInvokeDialog() - } + binding.selectTypeRadioGroup.setOnCheckedChangeListener { _, radioBtnId -> + setVisibilityEachView(radioBtnId) } + binding.sendBtn.setOnClickListener { showDialog() } + binding.shutdownSubscriptionBtn.setOnClickListener { showShutdownSubscriptionDialog() } - binding.addAttributeBtn.setOnClickListener { addPathList(ATTRIBUTE) } - binding.addEventBtn.setOnClickListener { addPathList(EVENT) } + binding.addAttributeBtn.setOnClickListener { addPathList(SendType.ATTRIBUTE) } + binding.addEventBtn.setOnClickListener { addPathList(SendType.EVENT) } + binding.addListBtn.setOnClickListener { addRequest() } binding.resetBtn.setOnClickListener { resetPath() } + binding.writeInvokeresetBtn.setOnClickListener { resetPath() } addressUpdateFragment = childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment @@ -165,6 +173,58 @@ class WildcardFragment : Fragment() { return binding.root } + private fun setVisibilityEachView(radioBtnId: Int) { + val readBtnOn = (radioBtnId == R.id.readRadioBtn) + val subscribeBtnOn = (radioBtnId == R.id.subscribeRadioBtn) + val writeBtnOn = (radioBtnId == R.id.writeRadioBtn) + val invokeBtnOn = (radioBtnId == R.id.invokeRadioBtn) + + binding.addAttributeBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.addEventBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.resetBtn.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.attributeIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) + binding.attributeIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn || writeBtnOn) + binding.eventIdLabel.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.eventIdEd.visibility = getVisibility(readBtnOn || subscribeBtnOn) + binding.addListBtn.visibility = getVisibility(writeBtnOn || invokeBtnOn) + binding.commandIdLabel.visibility = getVisibility(invokeBtnOn) + binding.commandIdEd.visibility = getVisibility(invokeBtnOn) + binding.isUrgentLabel.visibility = getVisibility(subscribeBtnOn) + binding.isUrgentSp.visibility = getVisibility(subscribeBtnOn) + binding.writeValueLabel.visibility = getVisibility(writeBtnOn) + binding.writeValueEd.visibility = getVisibility(writeBtnOn) + binding.writeValueTypeLabel.visibility = getVisibility(writeBtnOn) + binding.writeValueTypeSp.visibility = getVisibility(writeBtnOn) + binding.dataVersionLabel.visibility = getVisibility(writeBtnOn) + binding.dataVersionEd.visibility = getVisibility(writeBtnOn) + binding.invokeValueLabel.visibility = getVisibility(invokeBtnOn) + binding.invokeValueEd.visibility = getVisibility(invokeBtnOn) + binding.shutdownSubscriptionBtn.visibility = getVisibility(subscribeBtnOn) + binding.writeInvokeresetBtn.visibility = getVisibility(writeBtnOn || invokeBtnOn) + + resetPath() + } + + private fun showDialog() { + if (binding.readRadioBtn.isChecked) { + showReadDialog() + } else if (binding.subscribeRadioBtn.isChecked) { + showSubscribeDialog() + } else if (binding.writeRadioBtn.isChecked) { + showWriteDialog() + } else if (binding.invokeRadioBtn.isChecked) { + showInvokeDialog() + } + } + + private fun addRequest() { + if (binding.writeRadioBtn.isChecked) { + addWriteRequest() + } else { + addInvokeRequest() + } + } + private fun getVisibility(isShowing: Boolean): Int { return if (isShowing) { View.VISIBLE @@ -173,7 +233,7 @@ class WildcardFragment : Fragment() { } } - private fun addPathList(type: Int) { + private fun addPathList(type: SendType) { val endpointId = getChipPathIdForText(binding.endpointIdEd.text.toString()) val clusterId = getChipPathIdForText(binding.clusterIdEd.text.toString()) val attributeId = getChipPathIdForText(binding.attributeIdEd.text.toString()) @@ -183,9 +243,9 @@ class WildcardFragment : Fragment() { (binding.subscribeRadioBtn.isChecked) && (binding.isUrgentSp.selectedItem.toString().toBoolean()) - if (type == ATTRIBUTE) { + if (type == SendType.ATTRIBUTE) { attributePath.add(ChipAttributePath.newInstance(endpointId, clusterId, attributeId)) - } else if (type == EVENT) { + } else if (type == SendType.EVENT) { eventPath.add(ChipEventPath.newInstance(endpointId, clusterId, eventId, isUrgent)) } updateAddListView() @@ -194,6 +254,8 @@ class WildcardFragment : Fragment() { private fun resetPath() { attributePath.clear() eventPath.clear() + writePath.clear() + invokePath.clear() updateAddListView() } @@ -205,6 +267,12 @@ class WildcardFragment : Fragment() { for (event in eventPath) { builder.append("event($event)\n") } + for (write in writePath) { + builder.append("WritePath($write)\n") + } + for (invoke in invokePath) { + builder.append("InvokePath($invoke)\n") + } binding.sendListView.text = builder.toString() } @@ -312,13 +380,11 @@ class WildcardFragment : Fragment() { ) } - private suspend fun write( - writeValueType: String, - writeValue: String, - dataVersion: Int?, - timedRequestTimeoutMs: Int, - imTimeoutMs: Int - ) { + private fun addWriteRequest() { + val writeValue = binding.writeValueEd.text.toString() + val writeValueType = binding.writeValueTypeSp.selectedItem.toString() + val dataVersion = binding.dataVersionEd.text.toString() + val endpointId = if (!addressUpdateFragment.isGroupId()) { getChipPathIdForText(binding.endpointIdEd.text.toString()) @@ -329,10 +395,10 @@ class WildcardFragment : Fragment() { val attributeId = getChipPathIdForText(binding.attributeIdEd.text.toString()) val version = - if (dataVersion == null) { + if (dataVersion.isEmpty()) { Optional.empty() } else { - Optional.of(dataVersion) + Optional.of(dataVersion.toInt()) } lateinit var writeRequest: AttributeWriteRequest @@ -364,26 +430,15 @@ class WildcardFragment : Fragment() { version ) } - val devicePtr = - try { - addressUpdateFragment.getDevicePointer(requireContext()) - } catch (e: IllegalStateException) { - Log.d(TAG, "getDevicePointer exception", e) - return - } - deviceController.write( - writeAttributeCallback, - devicePtr, - listOf(writeRequest), - timedRequestTimeoutMs, - imTimeoutMs - ) + writePath.add(writeRequest) + updateAddListView() } - private suspend fun invoke(invokeJson: String, timedRequestTimeoutMs: Int, imTimeoutMs: Int) { + private fun addInvokeRequest() { val endpointId = getChipPathIdForText(binding.endpointIdEd.text.toString()) val clusterId = getChipPathIdForText(binding.clusterIdEd.text.toString()) val commandId = getChipPathIdForText(binding.commandIdEd.text.toString()) + val invokeJson = binding.invokeValueEd.text.toString() val jsonString = invokeJson.ifEmpty { "{}" } val invokeElement = @@ -398,6 +453,28 @@ class WildcardFragment : Fragment() { } else { InvokeElement.newInstance(endpointId, clusterId, commandId, null, jsonString) } + invokePath.add(invokeElement) + updateAddListView() + } + + private suspend fun write(timedRequestTimeoutMs: Int, imTimeoutMs: Int) { + val devicePtr = + try { + addressUpdateFragment.getDevicePointer(requireContext()) + } catch (e: IllegalStateException) { + Log.d(TAG, "getDevicePointer exception", e) + return + } + deviceController.write( + writeAttributeCallback, + devicePtr, + writePath, + timedRequestTimeoutMs, + imTimeoutMs + ) + } + + private suspend fun invoke(timedRequestTimeoutMs: Int, imTimeoutMs: Int) { val devicePtr = try { addressUpdateFragment.getDevicePointer(requireContext()) @@ -405,10 +482,10 @@ class WildcardFragment : Fragment() { Log.d(TAG, "getDevicePointer exception", e) return } - deviceController.invoke( - invokeCallback, + deviceController.extendableInvoke( + extendableInvokeCallback, devicePtr, - invokeElement, + invokePath, timedRequestTimeoutMs, imTimeoutMs ) @@ -453,31 +530,14 @@ class WildcardFragment : Fragment() { private fun showWriteDialog() { binding.outputTv.text = "" val dialogView = requireActivity().layoutInflater.inflate(R.layout.write_dialog, null) - val writeValueTypeSp = dialogView.findViewById(R.id.writeValueTypeSp) - val spinnerAdapter = - ArrayAdapter( - requireActivity(), - android.R.layout.simple_spinner_dropdown_item, - TLV_MAP.keys.toList() - ) - writeValueTypeSp.adapter = spinnerAdapter val dialog = AlertDialog.Builder(requireContext()).apply { setView(dialogView) }.create() dialogView.findViewById