From a444f2f5138da840921a4cb8ba6314a442a3f68a Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Sat, 4 Dec 2021 22:05:39 -0500 Subject: [PATCH 1/9] history tab done --- .../ClusterDetailFragment.kt | 200 ++++++++++++----- .../ClusterInteractionFragment.kt | 25 ++- .../ClusterInteractionHistoryFragment.kt | 72 +++++++ .../ClusterInteractionSettingsFragment.kt | 60 ++++++ .../clusterinteraction/EndpointAdapter.kt | 2 +- .../clusterinteraction/HistoryCommand.kt | 19 ++ .../HistoryCommandAdapter.kt | 203 ++++++++++++++++++ .../app/src/main/res/drawable/ic_history.xml | 10 + .../app/src/main/res/drawable/ic_settings.xml | 10 + .../layout/cluster_interaction_fragment.xml | 10 + .../cluster_interaction_history_fragment.xml | 35 +++ .../cluster_interaction_history_item.xml | 54 +++++ .../cluster_interaction_history_item_info.xml | 38 ++++ .../cluster_interaction_settings_fragment.xml | 13 ++ .../app/src/main/res/layout/endpoint_item.xml | 28 ++- .../cluster_interaction_bottom_navigation.xml | 12 ++ .../app/src/main/res/values/strings.xml | 1 + 17 files changed, 727 insertions(+), 65 deletions(-) create mode 100644 src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt create mode 100644 src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt create mode 100644 src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt create mode 100644 src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt create mode 100644 src/android/CHIPTool/app/src/main/res/drawable/ic_history.xml create mode 100644 src/android/CHIPTool/app/src/main/res/drawable/ic_settings.xml create mode 100644 src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_fragment.xml create mode 100644 src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item.xml create mode 100644 src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml create mode 100644 src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_settings_fragment.xml create mode 100644 src/android/CHIPTool/app/src/main/res/menu/cluster_interaction_bottom_navigation.xml 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 index 7371cce20365b9..1afe71f39e0839 100644 --- 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 @@ -7,6 +7,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView +import android.widget.Button import android.widget.LinearLayout import android.widget.Toast import androidx.appcompat.app.AlertDialog @@ -24,6 +25,8 @@ import chip.devicecontroller.ClusterInfoMapping import com.google.chip.chiptool.ChipClient import com.google.chip.chiptool.GenericChipDeviceListener import com.google.chip.chiptool.R +import com.google.chip.chiptool.clusterclient.clusterinteraction.ClusterInteractionHistoryFragment.Companion.clusterInteractionHistoryList +import kotlin.properties.Delegates import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackDataTv import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackNameTv import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackTypeTv @@ -49,45 +52,121 @@ class ClusterDetailFragment : Fragment() { get() = ChipClient.getDeviceController(requireContext()) private val scope = CoroutineScope(Dispatchers.Main + Job()) - private lateinit var clusterMap: Map - private lateinit var selectedClusterInfo: ClusterInfo - private lateinit var selectedCluster: ChipClusters.BaseChipCluster - private lateinit var selectedCommandCallback: DelegatedClusterCallback - private lateinit var selectedInteractionInfo: InteractionInfo - private var devicePtr = 0L - private var endpointId = 0 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - clusterMap = ClusterInfoMapping().clusterMap - devicePtr = checkNotNull(requireArguments().getLong(DEVICE_PTR_KEY)) endpointId = checkNotNull(requireArguments().getInt(ENDPOINT_ID_KEY)) + historyCommand = if (requireArguments().getSerializable(HISTORY_COMMAND) == null) { + HistoryCommand("", "", mutableListOf(), emptyMap(), "") + } else { + requireArguments().getSerializable(HISTORY_COMMAND) as HistoryCommand + } + Log.d(TAG, historyCommand.toString()) return inflater.inflate(R.layout.cluster_detail_fragment, container, false).apply { deviceController.setCompletionListener(GenericChipDeviceListener()) - commandAutoCompleteTv.visibility = View.GONE - clusterAutoCompleteSetup( + if (requireArguments().getSerializable(HISTORY_COMMAND) != null) { + autoComplete( + historyCommand, + clusterAutoCompleteTv, + commandAutoCompleteTv, + parameterList, + inflater, + callbackList + ) + } else { + commandAutoCompleteTv.visibility = View.GONE + clusterAutoCompleteSetup( + clusterAutoCompleteTv, + commandAutoCompleteTv, + parameterList, + callbackList + ) + commandAutoCompleteSetup(commandAutoCompleteTv, inflater, parameterList, callbackList) + } + setInvokeCommandOnClickListener( + invokeCommand, + callbackList, clusterAutoCompleteTv, commandAutoCompleteTv, - parameterList, - callbackList + parameterList ) - commandAutoCompleteSetup(commandAutoCompleteTv, inflater, parameterList, callbackList) - invokeCommand.setOnClickListener { - callbackList.removeAllViews() - val commandArguments = HashMap() - parameterList.forEach { - val type = - selectedInteractionInfo.commandParameters[it.clusterParameterNameTv.text.toString()]!!.type - val data = castStringToType(it.clusterParameterData.text.toString(), type)!! + } + } - commandArguments[it.clusterParameterNameTv.text.toString()] = data - } - selectedInteractionInfo.getCommandFunction() - .invokeCommand(selectedCluster, selectedCommandCallback, commandArguments) + private fun setInvokeCommandOnClickListener( + invokeCommand: Button, + callbackList: LinearLayout, + clusterAutoCompleteTv: AutoCompleteTextView, + commandAutoCompleteTv: AutoCompleteTextView, + parameterList: LinearLayout + ) { + invokeCommand.setOnClickListener { + callbackList.removeAllViews() + val commandArguments = HashMap() + clusterInteractionHistoryList.addFirst( + HistoryCommand( + clusterAutoCompleteTv.text.toString(), + commandAutoCompleteTv.text.toString(), + mutableListOf(), + null, + null + // need to pass list of parameter): String { + return if (castType == ByteArray::class.java) { + "Byte[]" + } else { + castType.toString() } } @@ -124,7 +203,7 @@ class ClusterDetailFragment : Fragment() { parameterList.removeAllViews() callbackList.removeAllViews() // populate all the commands that belong to the selected cluster - val selectedCluster: String = clusterAutoComplete.adapter.getItem(position).toString() + var selectedCluster: String = clusterAutoComplete.adapter.getItem(position).toString() val commandAdapter = getCommandOptions(selectedCluster) commandAutoComplete.setAdapter(commandAdapter) } @@ -145,26 +224,35 @@ class ClusterDetailFragment : Fragment() { selectedInteractionInfo = selectedClusterInfo.commands[selectedCommand]!! selectedCommandCallback = selectedInteractionInfo.commandCallbackSupplier.get() populateCommandParameter(inflater, parameterList) - selectedCommandCallback.setCallbackDelegate(object : ClusterCommandCallback { - override fun onSuccess(responseValues: Map) { - showMessage("Command success") - // Populate UI based on response values. We know the types from CommandInfo.getCommandResponses(). - requireActivity().runOnUiThread { - populateCallbackResult( - responseValues, - inflater, - callbackList - ) - } - responseValues.forEach { Log.d(TAG, it.toString()) } - } + setCallbackDelegate(inflater, callbackList) + } + } - override fun onFailure(exception: Exception) { - showMessage("Command failed") - Log.e(TAG, exception.toString()) + private fun setCallbackDelegate(inflater: LayoutInflater, callbackList: LinearLayout) { + selectedCommandCallback.setCallbackDelegate(object : ClusterCommandCallback { + override fun onSuccess(responseValues: Map) { + showMessage("Command success") + // Populate UI based on response values. We know the types from CommandInfo.getCommandResponses(). + requireActivity().runOnUiThread { + populateCallbackResult( + responseValues, + inflater, + callbackList, + ) } - }) - } + clusterInteractionHistoryList[0].responseValue = responseValues + clusterInteractionHistoryList[0].status = "Success" + responseValues.forEach { Log.d(TAG, it.toString()) } + } + + override fun onFailure(exception: Exception) { + showMessage("Command failed") + var errorStatus = exception.toString().split(':') + clusterInteractionHistoryList[0].status = + errorStatus[errorStatus.size - 2] + " " + errorStatus[errorStatus.size - 1] + Log.e(TAG, exception.toString()) + } + }) } private fun populateCommandParameter(inflater: LayoutInflater, parameterList: LinearLayout) { @@ -174,11 +262,7 @@ class ClusterDetailFragment : Fragment() { // byte[].class will be output as class [B, which is not readable, so dynamically change it // to Byte[]. If more custom logic is required, should add a className field in // commandParameterInfo - if (paramInfo.type == ByteArray::class.java) { - param.clusterParameterTypeTv.text = "Byte[]" - } else { - param.clusterParameterTypeTv.text = "${paramInfo.type}" - } + param.clusterParameterTypeTv.text = formatParameterType(paramInfo.type) parameterList.addView(param) } } @@ -274,16 +358,24 @@ class ClusterDetailFragment : Fragment() { companion object { private const val TAG = "ClusterDetailFragment" - private const val ENDPOINT_ID_KEY = "endpoint_id" - private const val DEVICE_PTR_KEY = "device_ptr" + private const val ENDPOINT_ID_KEY = "endpointId" + private const val HISTORY_COMMAND = "historyCommand" + private var clusterMap: Map = ClusterInfoMapping().clusterMap + private lateinit var selectedClusterInfo: ClusterInfo + private lateinit var selectedCluster: ChipClusters.BaseChipCluster + private lateinit var selectedCommandCallback: DelegatedClusterCallback + private lateinit var selectedInteractionInfo: InteractionInfo + private lateinit var historyCommand: HistoryCommand + private var devicePtr = ClusterInteractionFragment.devicePtr + var endpointId by Delegates.notNull() fun newInstance( - deviceId: Long, - endpointId: Int + endpointId: Int, + historyCommand: HistoryCommand? ): ClusterDetailFragment { return ClusterDetailFragment().apply { arguments = Bundle(2).apply { - putLong(DEVICE_PTR_KEY, deviceId) + putSerializable(HISTORY_COMMAND, historyCommand) putInt(ENDPOINT_ID_KEY, endpointId) } } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt index 1c3555009644de..44c9afd9c40719 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt @@ -9,12 +9,14 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import chip.devicecontroller.ChipDeviceController +import com.google.android.material.bottomnavigation.BottomNavigationView 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.cancel import com.google.chip.chiptool.clusterclient.AddressUpdateFragment +import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.bottomNavigationBar import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.endpointList import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.getEndpointListBtn import kotlinx.coroutines.launch @@ -25,7 +27,6 @@ class ClusterInteractionFragment : Fragment() { private lateinit var scope: CoroutineScope private lateinit var addressUpdateFragment: AddressUpdateFragment - private var devicePtr = 0L override fun onCreateView( inflater: LayoutInflater, @@ -55,6 +56,7 @@ class ClusterInteractionFragment : Fragment() { } endpointList.adapter = EndpointAdapter(dataList, EndpointListener()) endpointList.layoutManager = LinearLayoutManager(requireContext()) + bottomNavigationBar.setOnNavigationItemSelectedListener(bottomNavigationListener) } } @@ -71,6 +73,7 @@ class ClusterInteractionFragment : Fragment() { companion object { private const val TAG = "ClusterInteractionFragment" + var devicePtr = 0L fun newInstance(): ClusterInteractionFragment = ClusterInteractionFragment() } @@ -86,10 +89,26 @@ class ClusterInteractionFragment : Fragment() { fragmentTransaction.commit() } + private val bottomNavigationListener = BottomNavigationView.OnNavigationItemSelectedListener { menuItem -> + when (menuItem.itemId) { + R.id.clusterInteractionHistory -> { + val fragment = ClusterInteractionHistoryFragment() + showFragment(fragment) + return@OnNavigationItemSelectedListener true + } + R.id.clusterInteractionSettings -> { + val fragment = ClusterInteractionSettingsFragment() + showFragment(fragment) + return@OnNavigationItemSelectedListener true + } + } + false + } + inner class EndpointListener : EndpointAdapter.OnItemClickListener { override fun onItemClick(position: Int) { Toast.makeText(requireContext(), "Item $position clicked", Toast.LENGTH_SHORT).show() - showFragment(ClusterDetailFragment.newInstance(devicePtr, position)) + showFragment(ClusterDetailFragment.newInstance(position, null)) } } -} +} \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt new file mode 100644 index 00000000000000..d1368cdce5d9a1 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt @@ -0,0 +1,72 @@ +package com.google.chip.chiptool.clusterclient.clusterinteraction + +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.chip.chiptool.R +import kotlinx.android.synthetic.main.cluster_interaction_history_fragment.view.historyCommandList +import com.google.chip.chiptool.clusterclient.clusterinteraction.ClusterDetailFragment.Companion.endpointId + +/** + * A simple [Fragment] subclass. + * Use the [ClusterInteractionHistoryFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class ClusterInteractionHistoryFragment : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + Log.d(TAG, clusterInteractionHistoryList.toString()) + return inflater.inflate(R.layout.cluster_interaction_history_fragment, container, false).apply { + historyCommandList.adapter = + HistoryCommandAdapter(clusterInteractionHistoryList, HistoryCommandListener(), inflater) + historyCommandList.layoutManager = LinearLayoutManager(requireContext()) + } + } + + 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() + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment ClusterInteractionHistoryFragment. + */ + + private const val TAG = "ClusterInteractionHistoryFragment" + var clusterInteractionHistoryList = ArrayDeque() + fun newInstance() = + ClusterInteractionHistoryFragment().apply { + arguments = Bundle().apply { + } + } + } + + inner class HistoryCommandListener : HistoryCommandAdapter.OnItemClickListener { + override fun onItemClick(position: Int) { + Toast.makeText(requireContext(), "Item $position clicked", Toast.LENGTH_SHORT).show() + showFragment(ClusterDetailFragment.newInstance(endpointId, clusterInteractionHistoryList[position])) + } + } +} \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt new file mode 100644 index 00000000000000..4b3eeb2f855c52 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt @@ -0,0 +1,60 @@ +package com.google.chip.chiptool.clusterclient.clusterinteraction + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.chip.chiptool.R + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [ClusterInteractionSettingsFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class ClusterInteractionSettingsFragment : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.cluster_interaction_settings_fragment, container, false) + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment ClusterInteractionSettingsFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + ClusterInteractionSettingsFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +} \ 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 index 104cc1e14e0544..3a339a8a2b29f1 100644 --- 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 @@ -18,7 +18,7 @@ class EndpointAdapter( inner class EndpointViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { - val endpointId: TextView = itemView.findViewById(R.id.endpointTv) + val endpointId: TextView = itemView.findViewById(R.id.endpointNumberTv) init { itemView.setOnClickListener(this) diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt new file mode 100644 index 00000000000000..dab5c83fbb219b --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt @@ -0,0 +1,19 @@ +package com.google.chip.chiptool.clusterclient.clusterinteraction + +import chip.clusterinfo.CommandResponseInfo +import java.io.Serializable + +data class HistoryCommand( + val clusterName: String, + val commandName: String, + val parameterList: MutableList, + var responseValue: Map?, + var status: String? + // interactionInfo class should be enough here +) : Serializable + +data class HistoryParameterInfo( + val parameterName: String, + val parameterData: String, + val parameterType: Class<*> +) \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt new file mode 100644 index 00000000000000..c1e88cf06bd6c2 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt @@ -0,0 +1,203 @@ +package com.google.chip.chiptool.clusterclient.clusterinteraction + +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.RecyclerView +import chip.clusterinfo.CommandResponseInfo +import com.google.chip.chiptool.R +import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackDataTv +import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackNameTv +import kotlinx.android.synthetic.main.cluster_callback_item.view.clusterCallbackTypeTv +import kotlinx.android.synthetic.main.cluster_interaction_history_item_info.view.historyClusterNameTv +import kotlinx.android.synthetic.main.cluster_interaction_history_item_info.view.historyCommandNameTv +import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParameterData +import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParameterNameTv +import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParameterTypeTv + +/** + * EndpointAdapter implements the endpointList(RecycleView) Adapter and associates different + * endpoint with the same onClick function provided in [ClusterInteractionFragment.EndpointListener] + */ +class HistoryCommandAdapter( + private val HistoryCommandList: List, + private val listener: ClusterInteractionHistoryFragment.HistoryCommandListener, + private val inflater: LayoutInflater, +) : RecyclerView.Adapter() { + + inner class HistoryCommandViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), + View.OnClickListener { + var historyInfo: LinearLayout = itemView.findViewById(R.id.historyBasicInfo) + var parameterList: LinearLayout = itemView.findViewById(R.id.historyParameterList) + var responseValueList: LinearLayout = itemView.findViewById(R.id.historyResponseValueList) + var statusCode: LinearLayout = itemView.findViewById(R.id.historyItemStatus) + + 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): HistoryCommandViewHolder { + val itemView = + LayoutInflater.from(parent.context) + .inflate(R.layout.cluster_interaction_history_item, parent, false) + return HistoryCommandViewHolder(itemView) + } + + override fun getItemCount(): Int { + return HistoryCommandList.size + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getItemViewType(position: Int): Int { + return position + } + + override fun onBindViewHolder( + holder: HistoryCommandAdapter.HistoryCommandViewHolder, + position: Int + ) { + // go through each element and fill the data + // fill out cluster name and command name + clearPreviousReview(holder) + val info = inflater.inflate( + R.layout.cluster_interaction_history_item_info, + null, + false + ) as ConstraintLayout + info.historyClusterNameTv.text = HistoryCommandList[position].clusterName + info.historyCommandNameTv.text = HistoryCommandList[position].commandName + holder.historyInfo.addView(info) + // fill out parameterList + if (HistoryCommandList[position].parameterList.isEmpty()) { + val emptyParameterList = + inflater.inflate(android.R.layout.simple_list_item_1, null, false) as TextView + emptyParameterList.text = "No parameter" + holder.parameterList.addView(emptyParameterList) + } else { + HistoryCommandList[position].parameterList.forEach { + val param = + inflater.inflate(R.layout.cluster_parameter_item, null, false) as ConstraintLayout + param.clusterParameterData.setText(it.parameterData) + param.clusterParameterNameTv.text = it.parameterName + param.clusterParameterTypeTv.text = formatParameterType(it.parameterType) + holder.parameterList.addView(param) + } + } + // fill out responseList + if (HistoryCommandList[position].responseValue == null || HistoryCommandList[position].responseValue!!.isEmpty()) { + val emptyResponseInfo = + inflater.inflate(android.R.layout.simple_list_item_1, null, false) as TextView + emptyResponseInfo.text = "No response" + holder.responseValueList.addView(emptyResponseInfo) + } else { + populateCallbackResult( + HistoryCommandList[position].responseValue!!, + inflater, + holder.responseValueList + ) + } + // fill out status + val statusInfo = inflater.inflate(android.R.layout.simple_list_item_1, null, false) as TextView + statusInfo.text = "Status: " + HistoryCommandList[position].status + holder.statusCode.addView(statusInfo) + } + + private fun populateCallbackResult( + responseValues: Map, + inflater: LayoutInflater, + callbackList: LinearLayout + ) { + responseValues.forEach { (variableNameType, response) -> + if (response is List<*>) { + createListResponseView(response, inflater, callbackList, variableNameType) + } else { + createBasicResponseView(response, inflater, callbackList, variableNameType) + } + } + } + + private fun createBasicResponseView( + response: Any, + inflater: LayoutInflater, + callbackList: LinearLayout, + variableNameType: CommandResponseInfo + ) { + val callbackItem = + inflater.inflate(R.layout.cluster_callback_item, null, false) as ConstraintLayout + callbackItem.clusterCallbackNameTv.text = variableNameType.name + callbackItem.clusterCallbackDataTv.text = if (response.javaClass == ByteArray::class.java) { + (response as ByteArray).decodeToString() + } else { + response.toString() + } + callbackItem.clusterCallbackTypeTv.text = variableNameType.type + callbackList.addView(callbackItem) + } + + private fun createListResponseView( + response: List<*>, + inflater: LayoutInflater, + callbackList: LinearLayout, + variableNameType: CommandResponseInfo + ) { + if (response.isEmpty()) { + val emptyCallback = + inflater.inflate(R.layout.cluster_callback_item, null, false) as ConstraintLayout + emptyCallback.clusterCallbackNameTv.text = "Result is empty" + callbackList.addView(emptyCallback) + } else { + response.forEachIndexed { index, it -> + val attributeCallbackItem = + inflater.inflate(R.layout.cluster_callback_item, null, false) as ConstraintLayout + attributeCallbackItem.clusterCallbackNameTv.text = variableNameType.name + "[$index]" + val objectString = if (it!!.javaClass == ByteArray::class.java) { + (it as ByteArray).contentToString() + } else { + it.toString() + } + var callbackClassName = if (it!!.javaClass == ByteArray::class.java) { + "Byte[]" + } else { + it!!.javaClass.toString().split('$').last() + } + attributeCallbackItem.clusterCallbackDataTv.text = objectString + attributeCallbackItem.clusterCallbackTypeTv.text = "List<$callbackClassName>" + callbackList.addView(attributeCallbackItem) + } + } + } + + private fun formatParameterType(castType: Class<*>): String { + return if (castType == ByteArray::class.java) { + "Byte[]" + } else { + castType.toString() + } + } + + private fun clearPreviousReview(holder: HistoryCommandAdapter.HistoryCommandViewHolder) { + holder.historyInfo.removeAllViews() + holder.parameterList.removeAllViews() + holder.responseValueList.removeAllViews() + holder.statusCode.removeAllViews() + } +} \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/drawable/ic_history.xml b/src/android/CHIPTool/app/src/main/res/drawable/ic_history.xml new file mode 100644 index 00000000000000..b4c803f15eeaf1 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/drawable/ic_history.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/drawable/ic_settings.xml b/src/android/CHIPTool/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 00000000000000..2f249c9e8c6276 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,10 @@ + + + \ 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 2b50a2b988e526..8cb9ccfd954e7e 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 @@ -30,5 +30,15 @@ android:clipToPadding="false" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/getEndpointListBtn" /> + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_fragment.xml b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_fragment.xml new file mode 100644 index 00000000000000..df560633d8a7c5 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_fragment.xml @@ -0,0 +1,35 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item.xml b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item.xml new file mode 100644 index 00000000000000..0368d7b49dd4f9 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml new file mode 100644 index 00000000000000..536fae7e23d0a7 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml @@ -0,0 +1,38 @@ + + + + + + + + /> + + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_settings_fragment.xml b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_settings_fragment.xml new file mode 100644 index 00000000000000..49312d65d0c537 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_settings_fragment.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/layout/endpoint_item.xml b/src/android/CHIPTool/app/src/main/res/layout/endpoint_item.xml index ac93ca48438924..86ccc79dea7967 100644 --- a/src/android/CHIPTool/app/src/main/res/layout/endpoint_item.xml +++ b/src/android/CHIPTool/app/src/main/res/layout/endpoint_item.xml @@ -9,21 +9,35 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp" - android:layout_margin="4dp"> + android:layout_margin="4dp" + android:background="@color/cardview_shadow_start_color"> + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.25" /> + + diff --git a/src/android/CHIPTool/app/src/main/res/menu/cluster_interaction_bottom_navigation.xml b/src/android/CHIPTool/app/src/main/res/menu/cluster_interaction_bottom_navigation.xml new file mode 100644 index 00000000000000..c6b2a95369eead --- /dev/null +++ b/src/android/CHIPTool/app/src/main/res/menu/cluster_interaction_bottom_navigation.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/res/values/strings.xml b/src/android/CHIPTool/app/src/main/res/values/strings.xml index 093940527c9f55..2ba82c650ca3e4 100644 --- a/src/android/CHIPTool/app/src/main/res/values/strings.xml +++ b/src/android/CHIPTool/app/src/main/res/values/strings.xml @@ -151,4 +151,5 @@ Invoke Select a command Select a cluster + Endpoint: From 15d403024272926ea1f98277c44582242c844b13 Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Sun, 5 Dec 2021 00:14:10 -0500 Subject: [PATCH 2/9] polish pr --- .../CHIPTool/.idea/runConfigurations.xml | 12 ----------- .../ClusterDetailFragment.kt | 1 - .../ClusterInteractionHistoryFragment.kt | 5 +---- .../ClusterInteractionSettingsFragment.kt | 21 ++----------------- .../clusterinteraction/HistoryCommand.kt | 5 ++++- .../HistoryCommandAdapter.kt | 4 ++-- 6 files changed, 9 insertions(+), 39 deletions(-) delete mode 100644 src/android/CHIPTool/.idea/runConfigurations.xml diff --git a/src/android/CHIPTool/.idea/runConfigurations.xml b/src/android/CHIPTool/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d8b38ac..00000000000000 --- a/src/android/CHIPTool/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file 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 index 1afe71f39e0839..ee900eaac10391 100644 --- 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 @@ -113,7 +113,6 @@ class ClusterDetailFragment : Fragment() { mutableListOf(), null, null - // need to pass list of parameter, var responseValue: Map?, var status: String? - // interactionInfo class should be enough here ) : Serializable data class HistoryParameterInfo( diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt index c1e88cf06bd6c2..bf817b76c9dac6 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommandAdapter.kt @@ -20,8 +20,8 @@ import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParamet import kotlinx.android.synthetic.main.cluster_parameter_item.view.clusterParameterTypeTv /** - * EndpointAdapter implements the endpointList(RecycleView) Adapter and associates different - * endpoint with the same onClick function provided in [ClusterInteractionFragment.EndpointListener] + * HistoryCommandAdapter implements the historyCommandList(RecycleView) Adapter and associates different + * history command with the same onClick function provided in [ClusterInteractionHistoryFragment.HistoryCommandListener] */ class HistoryCommandAdapter( private val HistoryCommandList: List, From 8c3df2a9128040e65f03a9b7b595a7109485c1ee Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Mon, 6 Dec 2021 10:14:58 -0500 Subject: [PATCH 3/9] revert deleted file --- src/android/CHIPTool/.idea/runConfigurations.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/android/CHIPTool/.idea/runConfigurations.xml diff --git a/src/android/CHIPTool/.idea/runConfigurations.xml b/src/android/CHIPTool/.idea/runConfigurations.xml new file mode 100644 index 00000000000000..7f68460d8b38ac --- /dev/null +++ b/src/android/CHIPTool/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file From 1c0221b3895bfdc77f687ab877f05dd38044486b Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Mon, 6 Dec 2021 10:19:51 -0500 Subject: [PATCH 4/9] further polish pr --- .../clusterclient/clusterinteraction/ClusterDetailFragment.kt | 2 +- .../clusterinteraction/ClusterInteractionSettingsFragment.kt | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) 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 index ee900eaac10391..5c52379a438d75 100644 --- 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 @@ -202,7 +202,7 @@ class ClusterDetailFragment : Fragment() { parameterList.removeAllViews() callbackList.removeAllViews() // populate all the commands that belong to the selected cluster - var selectedCluster: String = clusterAutoComplete.adapter.getItem(position).toString() + val selectedCluster: String = clusterAutoComplete.adapter.getItem(position).toString() val commandAdapter = getCommandOptions(selectedCluster) commandAutoComplete.setAdapter(commandAdapter) } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt index 90d1d6e5d5658e..eacd72cf8aac7f 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt @@ -13,9 +13,6 @@ import com.google.chip.chiptool.R * create an instance of this fragment. */ class ClusterInteractionSettingsFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, From 326a7341999d6d4e253988d474d9340bce361e68 Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Mon, 6 Dec 2021 12:04:09 -0500 Subject: [PATCH 5/9] remove todo --- .../clusterinteraction/ClusterInteractionSettingsFragment.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt index eacd72cf8aac7f..25657f23d3fe45 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt @@ -29,8 +29,6 @@ class ClusterInteractionSettingsFragment : Fragment() { * * @return A new instance of fragment ClusterInteractionSettingsFragment. */ - // TODO: Rename and change types and number of parameters - @JvmStatic fun newInstance() = ClusterInteractionSettingsFragment().apply { arguments = Bundle().apply { From 64d085404499405fc8b92968f4c20309d7a97829 Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Mon, 6 Dec 2021 21:10:34 -0500 Subject: [PATCH 6/9] improve based on comments --- .../ClusterDetailFragment.kt | 37 +++++++++---------- .../ClusterInteractionHistoryFragment.kt | 21 +++++------ .../ClusterInteractionSettingsFragment.kt | 12 +----- 3 files changed, 27 insertions(+), 43 deletions(-) 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 index 5c52379a438d75..ce3636c1755f7c 100644 --- 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 @@ -52,6 +52,17 @@ class ClusterDetailFragment : Fragment() { get() = ChipClient.getDeviceController(requireContext()) private val scope = CoroutineScope(Dispatchers.Main + Job()) + private var clusterMap: Map = ClusterInfoMapping().clusterMap + private lateinit var selectedClusterInfo: ClusterInfo + private lateinit var selectedCluster: ChipClusters.BaseChipCluster + private lateinit var selectedCommandCallback: DelegatedClusterCallback + private lateinit var selectedInteractionInfo: InteractionInfo + + // when user opens detail page from home page of cluster interaction, historyCommand will be + // null, and nothing will be autocompleted. If the detail page is opened from history page, + // cluster name, command name and potential parameter list will be filled out based on historyCommand + private var historyCommand: HistoryCommand? = null + private var devicePtr = ClusterInteractionFragment.devicePtr override fun onCreateView( inflater: LayoutInflater, @@ -59,17 +70,12 @@ class ClusterDetailFragment : Fragment() { savedInstanceState: Bundle? ): View { endpointId = checkNotNull(requireArguments().getInt(ENDPOINT_ID_KEY)) - historyCommand = if (requireArguments().getSerializable(HISTORY_COMMAND) == null) { - HistoryCommand("", "", mutableListOf(), emptyMap(), "") - } else { - requireArguments().getSerializable(HISTORY_COMMAND) as HistoryCommand - } - Log.d(TAG, historyCommand.toString()) + historyCommand = requireArguments().getSerializable(HISTORY_COMMAND) as HistoryCommand return inflater.inflate(R.layout.cluster_detail_fragment, container, false).apply { deviceController.setCompletionListener(GenericChipDeviceListener()) - if (requireArguments().getSerializable(HISTORY_COMMAND) != null) { - autoComplete( - historyCommand, + if (historyCommand != null) { + autoCompleteBasedOnHistoryCommand( + historyCommand!!, clusterAutoCompleteTv, commandAutoCompleteTv, parameterList, @@ -134,7 +140,8 @@ class ClusterDetailFragment : Fragment() { } } - private fun autoComplete( + // Cluster name, command name and parameter list will be autofill based on the given historyCommand + private fun autoCompleteBasedOnHistoryCommand( historyCommand: HistoryCommand, clusterAutoComplete: AutoCompleteTextView, commandAutoComplete: AutoCompleteTextView, @@ -152,9 +159,6 @@ class ClusterDetailFragment : Fragment() { historyCommand.parameterList.forEach { val param = inflater.inflate(R.layout.cluster_parameter_item, null, false) as ConstraintLayout param.clusterParameterNameTv.text = "${it.parameterName}" - // byte[].class will be output as class [B, which is not readable, so dynamically change it - // to Byte[]. If more custom logic is required, should add a className field in - // commandParameterInfo param.clusterParameterTypeTv.text = formatParameterType(it.parameterType) param.clusterParameterData.setText(it.parameterData) parameterList.addView(param) @@ -359,13 +363,6 @@ class ClusterDetailFragment : Fragment() { private const val TAG = "ClusterDetailFragment" private const val ENDPOINT_ID_KEY = "endpointId" private const val HISTORY_COMMAND = "historyCommand" - private var clusterMap: Map = ClusterInfoMapping().clusterMap - private lateinit var selectedClusterInfo: ClusterInfo - private lateinit var selectedCluster: ChipClusters.BaseChipCluster - private lateinit var selectedCommandCallback: DelegatedClusterCallback - private lateinit var selectedInteractionInfo: InteractionInfo - private lateinit var historyCommand: HistoryCommand - private var devicePtr = ClusterInteractionFragment.devicePtr var endpointId by Delegates.notNull() fun newInstance( diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt index 8774da6ed5ebb6..6ec119d7178cca 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt @@ -45,25 +45,22 @@ class ClusterInteractionHistoryFragment : Fragment() { } companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * @return A new instance of fragment ClusterInteractionHistoryFragment. - */ - private const val TAG = "ClusterInteractionHistoryFragment" + // The history list is a most-recent-first, therefore adding the most recent executed + // command on the top of the list var clusterInteractionHistoryList = ArrayDeque() fun newInstance() = - ClusterInteractionHistoryFragment().apply { - arguments = Bundle().apply { - } - } + ClusterInteractionHistoryFragment() } inner class HistoryCommandListener : HistoryCommandAdapter.OnItemClickListener { override fun onItemClick(position: Int) { - Toast.makeText(requireContext(), "Item $position clicked", Toast.LENGTH_SHORT).show() - showFragment(ClusterDetailFragment.newInstance(endpointId, clusterInteractionHistoryList[position])) + showFragment( + ClusterDetailFragment.newInstance( + endpointId, + clusterInteractionHistoryList[position] + ) + ) } } } \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt index 25657f23d3fe45..5a535c0025671e 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionSettingsFragment.kt @@ -23,16 +23,6 @@ class ClusterInteractionSettingsFragment : Fragment() { } companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @return A new instance of fragment ClusterInteractionSettingsFragment. - */ - fun newInstance() = - ClusterInteractionSettingsFragment().apply { - arguments = Bundle().apply { - } - } + fun newInstance() = ClusterInteractionSettingsFragment() } } \ No newline at end of file From eeb208648246d724d8a6db35e9f7f8a271cf6749 Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Thu, 9 Dec 2021 19:48:06 -0500 Subject: [PATCH 7/9] resolve static variable conflict --- .../clusterinteraction/ClusterDetailFragment.kt | 15 ++++++++++++--- .../ClusterInteractionFragment.kt | 16 ++++++---------- .../ClusterInteractionHistoryFragment.kt | 7 +++---- .../clusterinteraction/HistoryCommand.kt | 4 +++- .../cluster_interaction_history_item_info.xml | 2 +- 5 files changed, 25 insertions(+), 19 deletions(-) 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 index ce3636c1755f7c..0c2e1893d45acf 100644 --- 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 @@ -57,18 +57,23 @@ class ClusterDetailFragment : Fragment() { private lateinit var selectedCluster: ChipClusters.BaseChipCluster private lateinit var selectedCommandCallback: DelegatedClusterCallback private lateinit var selectedInteractionInfo: InteractionInfo + private var devicePtr by Delegates.notNull() + private var endpointId by Delegates.notNull() + // when user opens detail page from home page of cluster interaction, historyCommand will be // null, and nothing will be autocompleted. If the detail page is opened from history page, // cluster name, command name and potential parameter list will be filled out based on historyCommand private var historyCommand: HistoryCommand? = null - private var devicePtr = ClusterInteractionFragment.devicePtr + + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { + devicePtr = checkNotNull(requireArguments().getLong(DEVICE_PTR)) endpointId = checkNotNull(requireArguments().getInt(ENDPOINT_ID_KEY)) historyCommand = requireArguments().getSerializable(HISTORY_COMMAND) as HistoryCommand return inflater.inflate(R.layout.cluster_detail_fragment, container, false).apply { @@ -118,7 +123,9 @@ class ClusterDetailFragment : Fragment() { commandAutoCompleteTv.text.toString(), mutableListOf(), null, - null + null, + endpointId, + devicePtr ) ) parameterList.forEach { @@ -363,14 +370,16 @@ class ClusterDetailFragment : Fragment() { private const val TAG = "ClusterDetailFragment" private const val ENDPOINT_ID_KEY = "endpointId" private const val HISTORY_COMMAND = "historyCommand" - var endpointId by Delegates.notNull() + private const val DEVICE_PTR = "devicePtr" fun newInstance( + devicePtr: Long, endpointId: Int, historyCommand: HistoryCommand? ): ClusterDetailFragment { return ClusterDetailFragment().apply { arguments = Bundle(2).apply { + putLong(DEVICE_PTR, devicePtr) putSerializable(HISTORY_COMMAND, historyCommand) putInt(ENDPOINT_ID_KEY, endpointId) } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt index 44c9afd9c40719..0e9a7c483a7e44 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt @@ -16,6 +16,7 @@ import com.google.chip.chiptool.R import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import com.google.chip.chiptool.clusterclient.AddressUpdateFragment +import kotlin.properties.Delegates import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.bottomNavigationBar import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.endpointList import kotlinx.android.synthetic.main.cluster_interaction_fragment.view.getEndpointListBtn @@ -27,6 +28,7 @@ class ClusterInteractionFragment : Fragment() { private lateinit var scope: CoroutineScope private lateinit var addressUpdateFragment: AddressUpdateFragment + private var devicePtr by Delegates.notNull() override fun onCreateView( inflater: LayoutInflater, @@ -73,26 +75,21 @@ class ClusterInteractionFragment : Fragment() { companion object { private const val TAG = "ClusterInteractionFragment" - var devicePtr = 0L fun newInstance(): ClusterInteractionFragment = ClusterInteractionFragment() } - private fun showFragment(fragment: Fragment, showOnBack: Boolean = true) { + private fun showFragment(fragment: Fragment) { val fragmentTransaction = requireActivity().supportFragmentManager .beginTransaction() .replace(R.id.fragment_container, fragment, fragment.javaClass.simpleName) - - if (showOnBack) { - fragmentTransaction.addToBackStack(null) - } - + fragmentTransaction.addToBackStack(null) fragmentTransaction.commit() } private val bottomNavigationListener = BottomNavigationView.OnNavigationItemSelectedListener { menuItem -> when (menuItem.itemId) { R.id.clusterInteractionHistory -> { - val fragment = ClusterInteractionHistoryFragment() + val fragment = ClusterInteractionHistoryFragment.newInstance() showFragment(fragment) return@OnNavigationItemSelectedListener true } @@ -107,8 +104,7 @@ class ClusterInteractionFragment : Fragment() { inner class EndpointListener : EndpointAdapter.OnItemClickListener { override fun onItemClick(position: Int) { - Toast.makeText(requireContext(), "Item $position clicked", Toast.LENGTH_SHORT).show() - showFragment(ClusterDetailFragment.newInstance(position, null)) + showFragment(ClusterDetailFragment.newInstance(devicePtr, position, null)) } } } \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt index 6ec119d7178cca..c580d2cf81a089 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt @@ -6,11 +6,9 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.recyclerview.widget.LinearLayoutManager import com.google.chip.chiptool.R import kotlinx.android.synthetic.main.cluster_interaction_history_fragment.view.historyCommandList -import com.google.chip.chiptool.clusterclient.clusterinteraction.ClusterDetailFragment.Companion.endpointId /** * A simple [Fragment] subclass for the cluster interaction history component @@ -48,7 +46,7 @@ class ClusterInteractionHistoryFragment : Fragment() { private const val TAG = "ClusterInteractionHistoryFragment" // The history list is a most-recent-first, therefore adding the most recent executed // command on the top of the list - var clusterInteractionHistoryList = ArrayDeque() + val clusterInteractionHistoryList = ArrayDeque() fun newInstance() = ClusterInteractionHistoryFragment() } @@ -57,7 +55,8 @@ class ClusterInteractionHistoryFragment : Fragment() { override fun onItemClick(position: Int) { showFragment( ClusterDetailFragment.newInstance( - endpointId, + clusterInteractionHistoryList[position].deviceptr, + clusterInteractionHistoryList[position].endpointId, clusterInteractionHistoryList[position] ) ) diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt index 12df660c7df6d2..bdf9d065f76c3b 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt @@ -12,7 +12,9 @@ data class HistoryCommand( val commandName: String, val parameterList: MutableList, var responseValue: Map?, - var status: String? + var status: String?, + var endpointId: Int, + var deviceptr: Long, ) : Serializable data class HistoryParameterInfo( diff --git a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml index 536fae7e23d0a7..4087b580cda4d6 100644 --- a/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml +++ b/src/android/CHIPTool/app/src/main/res/layout/cluster_interaction_history_item_info.xml @@ -32,7 +32,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/historyClusterNameTv" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintWidth_percent="0.5" /> + app:layout_constraintWidth_percent="0.6" /> /> \ No newline at end of file From 1ab13c8c34c6b537d23fe0c29c40c4aca889e287 Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Fri, 10 Dec 2021 10:42:12 -0500 Subject: [PATCH 8/9] store deviceId instead of devicePtr --- .../ClusterDetailFragment.kt | 26 ++++++++++++------- .../ClusterInteractionFragment.kt | 2 +- .../ClusterInteractionHistoryFragment.kt | 2 +- .../clusterinteraction/HistoryCommand.kt | 2 +- 4 files changed, 20 insertions(+), 12 deletions(-) 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 index 0c2e1893d45acf..cd90bff7624b04 100644 --- 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 @@ -14,6 +14,7 @@ import androidx.appcompat.app.AlertDialog import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.forEach import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import chip.clusterinfo.ClusterCommandCallback import chip.clusterinfo.ClusterInfo import chip.clusterinfo.InteractionInfo @@ -23,6 +24,7 @@ import chip.devicecontroller.ChipClusters import chip.devicecontroller.ChipDeviceController import chip.devicecontroller.ClusterInfoMapping import com.google.chip.chiptool.ChipClient +import com.google.chip.chiptool.ChipClient.getConnectedDevicePointer import com.google.chip.chiptool.GenericChipDeviceListener import com.google.chip.chiptool.R import com.google.chip.chiptool.clusterclient.clusterinteraction.ClusterInteractionHistoryFragment.Companion.clusterInteractionHistoryList @@ -42,6 +44,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch /** * ClusterDetailFragment allows user to pick cluster, command, specify parameters and see @@ -51,13 +54,14 @@ class ClusterDetailFragment : Fragment() { private val deviceController: ChipDeviceController get() = ChipClient.getDeviceController(requireContext()) - private val scope = CoroutineScope(Dispatchers.Main + Job()) + private lateinit var scope: CoroutineScope private var clusterMap: Map = ClusterInfoMapping().clusterMap private lateinit var selectedClusterInfo: ClusterInfo private lateinit var selectedCluster: ChipClusters.BaseChipCluster private lateinit var selectedCommandCallback: DelegatedClusterCallback private lateinit var selectedInteractionInfo: InteractionInfo private var devicePtr by Delegates.notNull() + private var deviceId by Delegates.notNull() private var endpointId by Delegates.notNull() @@ -73,9 +77,13 @@ class ClusterDetailFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - devicePtr = checkNotNull(requireArguments().getLong(DEVICE_PTR)) + scope = viewLifecycleOwner.lifecycleScope + deviceId = checkNotNull(requireArguments().getLong(DEVICE_ID)) + scope.launch { + devicePtr = getConnectedDevicePointer(requireContext(), deviceId) + } endpointId = checkNotNull(requireArguments().getInt(ENDPOINT_ID_KEY)) - historyCommand = requireArguments().getSerializable(HISTORY_COMMAND) as HistoryCommand + historyCommand = requireArguments().getSerializable(HISTORY_COMMAND) as HistoryCommand? return inflater.inflate(R.layout.cluster_detail_fragment, container, false).apply { deviceController.setCompletionListener(GenericChipDeviceListener()) if (historyCommand != null) { @@ -125,7 +133,7 @@ class ClusterDetailFragment : Fragment() { null, null, endpointId, - devicePtr + deviceId ) ) parameterList.forEach { @@ -335,7 +343,7 @@ class ClusterDetailFragment : Fragment() { } else { it!!.javaClass.toString().split('$').last() } - attributeCallbackItem.clusterCallbackDataTv.text = objectString + attributeCallbackItem.clusterCallbackDataTv.text = callbackClassName attributeCallbackItem.clusterCallbackDataTv.setOnClickListener { AlertDialog.Builder(requireContext()) .setTitle(callbackClassName) @@ -370,16 +378,16 @@ class ClusterDetailFragment : Fragment() { private const val TAG = "ClusterDetailFragment" private const val ENDPOINT_ID_KEY = "endpointId" private const val HISTORY_COMMAND = "historyCommand" - private const val DEVICE_PTR = "devicePtr" + private const val DEVICE_ID = "deviceId" fun newInstance( - devicePtr: Long, + deviceId: Long, endpointId: Int, historyCommand: HistoryCommand? ): ClusterDetailFragment { return ClusterDetailFragment().apply { - arguments = Bundle(2).apply { - putLong(DEVICE_PTR, devicePtr) + arguments = Bundle(4).apply { + putLong(DEVICE_ID, deviceId) putSerializable(HISTORY_COMMAND, historyCommand) putInt(ENDPOINT_ID_KEY, endpointId) } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt index 0e9a7c483a7e44..950c270b4d43fa 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionFragment.kt @@ -104,7 +104,7 @@ class ClusterInteractionFragment : Fragment() { inner class EndpointListener : EndpointAdapter.OnItemClickListener { override fun onItemClick(position: Int) { - showFragment(ClusterDetailFragment.newInstance(devicePtr, position, null)) + showFragment(ClusterDetailFragment.newInstance(addressUpdateFragment.deviceId, position, null)) } } } \ No newline at end of file diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt index c580d2cf81a089..3eaa874e2c6013 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/ClusterInteractionHistoryFragment.kt @@ -55,7 +55,7 @@ class ClusterInteractionHistoryFragment : Fragment() { override fun onItemClick(position: Int) { showFragment( ClusterDetailFragment.newInstance( - clusterInteractionHistoryList[position].deviceptr, + clusterInteractionHistoryList[position].deviceId, clusterInteractionHistoryList[position].endpointId, clusterInteractionHistoryList[position] ) diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt index bdf9d065f76c3b..5105d458ec5512 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/clusterinteraction/HistoryCommand.kt @@ -14,7 +14,7 @@ data class HistoryCommand( var responseValue: Map?, var status: String?, var endpointId: Int, - var deviceptr: Long, + var deviceId: Long, ) : Serializable data class HistoryParameterInfo( From bb5aa0038395c690071ca621d27b7cfadd71b43e Mon Sep 17 00:00:00 2001 From: JasonLiuZhuoCheng Date: Fri, 10 Dec 2021 18:03:48 -0500 Subject: [PATCH 9/9] Update ClusterDetailFragment.kt --- .../clusterinteraction/ClusterDetailFragment.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 index cd90bff7624b04..87321a93c40211 100644 --- 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 @@ -64,14 +64,11 @@ class ClusterDetailFragment : Fragment() { private var deviceId by Delegates.notNull() private var endpointId by Delegates.notNull() - // when user opens detail page from home page of cluster interaction, historyCommand will be // null, and nothing will be autocompleted. If the detail page is opened from history page, // cluster name, command name and potential parameter list will be filled out based on historyCommand private var historyCommand: HistoryCommand? = null - - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -394,4 +391,4 @@ class ClusterDetailFragment : Fragment() { } } } -} \ No newline at end of file +}