diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt index f40f7e6438b952..fa906cbd3c1d48 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt @@ -31,7 +31,7 @@ import chip.setuppayload.SetupPayload import chip.setuppayload.SetupPayloadParser import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException import com.google.chip.chiptool.attestation.AttestationTestFragment -import com.google.chip.chiptool.clusterclient.ClusterInteractionFragment +import com.google.chip.chiptool.clusterclient.clusterinteraction.ClusterInteractionFragment import com.google.chip.chiptool.clusterclient.MultiAdminClientFragment import com.google.chip.chiptool.clusterclient.OpCredClientFragment import com.google.chip.chiptool.clusterclient.BasicClientFragment diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterDetailFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterDetailFragment.kt new file mode 100644 index 00000000000000..b0c26054a00749 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterDetailFragment.kt @@ -0,0 +1,53 @@ +package com.google.chip.chiptool.clusterclient.clusterinteraction + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import chip.devicecontroller.ChipDeviceController +import com.google.chip.chiptool.ChipClient +import com.google.chip.chiptool.GenericChipDeviceListener +import com.google.chip.chiptool.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel + +/** + * ClusterDetailFragment allows user to pick cluster, command, specify parameters and see + * the callback result. + */ +class ClusterDetailFragment : Fragment(){ + private val deviceController: ChipDeviceController + get() = ChipClient.getDeviceController(requireContext()) + + private val scope = CoroutineScope(Dispatchers.Main + Job()) + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.cluster_detail_fragment, container, false).apply { + deviceController.setCompletionListener(GenericChipDeviceListener()) + } + } + + private fun showMessage(msg: String) { + requireActivity().runOnUiThread { + Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show() + } + } + + override fun onStop() { + super.onStop() + scope.cancel() + } + + companion object { + private const val TAG = "ClusterDetailFragment" + fun newInstance(): ClusterDetailFragment = ClusterDetailFragment() + } +} \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ClusterInteractionFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt similarity index 58% rename from src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ClusterInteractionFragment.kt rename to src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt index 96d171b9333ea9..7bcd8cf5fc11b5 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ClusterInteractionFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt @@ -1,4 +1,4 @@ -package com.google.chip.chiptool.clusterclient +package com.google.chip.chiptool.clusterclient.clusterinteraction import android.os.Bundle import android.util.Log @@ -7,9 +7,8 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.Fragment -import chip.clusterinfo.ClusterCommandCallback +import androidx.recyclerview.widget.LinearLayoutManager import chip.clusterinfo.ClusterInfo -import chip.clusterinfo.CommandInfo import chip.devicecontroller.ChipDeviceController import com.google.chip.chiptool.ChipClient import com.google.chip.chiptool.GenericChipDeviceListener @@ -19,8 +18,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import chip.devicecontroller.ClusterInfoMapping -import java.lang.Exception -import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.getClusterMappingBtn +import com.google.chip.chiptool.clusterclient.AddressUpdateFragment +import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.endpointList +import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.getEndpointListBtn import kotlinx.coroutines.launch class ClusterInteractionFragment : Fragment() { @@ -38,41 +38,25 @@ class ClusterInteractionFragment : Fragment() { ): View { return inflater.inflate(R.layout.cluster_interaction_fragment, container, false).apply { deviceController.setCompletionListener(ChipControllerCallback()) - addressUpdateFragment = - childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment - clusterMap = ClusterInfoMapping().clusterMap; - getClusterMappingBtn.setOnClickListener { scope.launch { getClusterMapping() } } - } - } - - private suspend fun getClusterMapping() { - // In real code: "OnOff" would be selected by the user. - val methodSelected = "onOff" - showMessage(methodSelected + " is initialized") - val selectedClusterInfo = clusterMap[methodSelected]!! - val devicePtr = - ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId) - val endpointId = 1 - val selectedCluster = selectedClusterInfo.createClusterFunction.create(devicePtr, endpointId) - // Imagine user wants to execute the command "OffWithEffect", pass the string here - val selectedCommandInfo: CommandInfo = selectedClusterInfo.commands["on"]!! - - var selectedCommandCallback = selectedCommandInfo.commandCallbackSupplier.get() - selectedCommandCallback?.setCallbackDelegate(object : ClusterCommandCallback { - override fun onSuccess(responseValues: List) { - showMessage("Command success") - // Populate UI based on response values. We know the types from CommandInfo.getCommandResponses(). - responseValues.forEach { Log.d(TAG, it.toString()) } + getEndpointListBtn.setOnClickListener { + scope.launch { + showMessage("Retrieving endpoints") + endpointList.visibility = View.VISIBLE + } } - override fun onFailure(exception: Exception) { - showMessage("Command failed") - Log.e(TAG, exception.toString()) + addressUpdateFragment = + childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment + clusterMap = ClusterInfoMapping().clusterMap + var dataList: List = ArrayList() + // TODO: Dynamically retrieve endpoint information using descriptor cluster + // hardcode the endpoint for now + for (i in 0 until 2) { + dataList += EndpointItem(i) } - }) - - var commandArguments: HashMap = HashMap() - selectedCommandInfo.getCommandFunction().invokeCommand(selectedCluster, selectedCommandCallback, commandArguments) + endpointList.adapter = EndpointAdapter(dataList, EndpointListener()) + endpointList.layoutManager = LinearLayoutManager(requireContext()) + } } private fun showMessage(msg: String) { @@ -109,4 +93,23 @@ class ClusterInteractionFragment : Fragment() { private const val TAG = "ClusterInteractionFragment" fun newInstance(): ClusterInteractionFragment = ClusterInteractionFragment() } + + private fun showFragment(fragment: Fragment, showOnBack: Boolean = true) { + val fragmentTransaction = requireActivity().supportFragmentManager + .beginTransaction() + .replace(R.id.fragment_container, fragment, fragment.javaClass.simpleName) + + if (showOnBack) { + fragmentTransaction.addToBackStack(null) + } + + fragmentTransaction.commit() + } + + inner class EndpointListener : EndpointAdapter.OnItemClickListener { + override fun onItemClick(position: Int) { + Toast.makeText(requireContext(), "Item $position clicked", Toast.LENGTH_SHORT).show() + showFragment(ClusterDetailFragment.newInstance()) + } + } } \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/EndpointAdapter.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/EndpointAdapter.kt new file mode 100644 index 00000000000000..104cc1e14e0544 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/EndpointAdapter.kt @@ -0,0 +1,52 @@ +package com.google.chip.chiptool.clusterclient.clusterinteraction + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.google.chip.chiptool.R + +/** + * EndpointAdapter implements the endpointList(RecycleView) Adapter and associates different + * endpoint with the same onClick function provided in [ClusterInteractionFragment.EndpointListener] + */ +class EndpointAdapter( + private val endpointList: List, + private val listener: OnItemClickListener +) : RecyclerView.Adapter() { + + inner class EndpointViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), + View.OnClickListener { + val endpointId: TextView = itemView.findViewById(R.id.endpointTv) + + init { + itemView.setOnClickListener(this) + } + + override fun onClick(endpointItem: View) { + val position = this.adapterPosition + if (position != RecyclerView.NO_POSITION) { + listener.onItemClick(position) + } + } + } + + interface OnItemClickListener { + fun onItemClick(position: Int) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EndpointViewHolder { + val itemView = + LayoutInflater.from(parent.context).inflate(R.layout.endpoint_item, parent, false) + return EndpointViewHolder(itemView) + } + + override fun onBindViewHolder(holder: EndpointViewHolder, position: Int) { + holder.endpointId.text = endpointList[position].endpointId.toString() + } + + override fun getItemCount(): Int { + return endpointList.size + } +} \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/endpointItem.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/endpointItem.kt new file mode 100644 index 00000000000000..be33d661e871e7 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/endpointItem.kt @@ -0,0 +1,3 @@ +package com.google.chip.chiptool.clusterclient.clusterinteraction + +data class EndpointItem(val endpointId: Int) \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/layout/cluster_detail_fragment.xml b/src/android/CHIPTool/app/src/main/res/layout/cluster_detail_fragment.xml new file mode 100644 index 00000000000000..df7ab6283f1ef4 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/layout/cluster_detail_fragment.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_fragment.xml b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_fragment.xml index f101f9d7626204..2b50a2b988e526 100644 --- a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_fragment.xml +++ b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_fragment.xml @@ -7,12 +7,12 @@ android:layout_height="match_parent">