Skip to content

Commit

Permalink
[Android] Custom CommissioningFlow Implementation (#11101)
Browse files Browse the repository at this point in the history
* Add Commissioning Flow = 2 (Custom) in Android app

* Resolve PR comments by austinh0
  • Loading branch information
lijujayakumar authored and pull[bot] committed Aug 15, 2023
1 parent c13ff07 commit 3d7c25b
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/android/CHIPTool/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ android {
}

dependencies {
implementation 'com.android.volley:volley:1.2.1'
def room_version = "2.2.5"

implementation "androidx.room:room-runtime:$room_version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.google.chip.chiptool

import android.content.Intent
import android.net.Uri
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.os.Bundle
Expand All @@ -42,12 +43,15 @@ import com.google.chip.chiptool.provisioning.ProvisionNetworkType
import com.google.chip.chiptool.setuppayloadscanner.BarcodeFragment
import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceDetailsFragment
import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo
import com.google.chip.chiptool.setuppayloadscanner.CHIPLedgerDetailsFragment

class CHIPToolActivity :
AppCompatActivity(),
BarcodeFragment.Callback,
SelectActionFragment.Callback,
DeviceProvisioningFragment.Callback {
DeviceProvisioningFragment.Callback,
CHIPDeviceDetailsFragment.Callback,
CHIPLedgerDetailsFragment.Callback {

private var networkType: ProvisionNetworkType? = null

Expand Down Expand Up @@ -141,6 +145,15 @@ class CHIPToolActivity :
showFragment(AttestationTestFragment.newInstance())
}

override fun handleReadFromLedgerClicked(deviceInfo: CHIPDeviceInfo) {
showFragment(CHIPLedgerDetailsFragment.newInstance(deviceInfo))
}

override fun handleCustomFlowRedirectClicked(redirectUrl: String) {
val redirectIntent = Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl))
startActivity(redirectIntent)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

Expand All @@ -150,6 +163,10 @@ class CHIPToolActivity :
}
}

override fun handleCustomFlowClicked() {
showFragment(BarcodeFragment.newInstance())
}

private fun showFragment(fragment: Fragment, showOnBack: Boolean = true) {
val fragmentTransaction = supportFragmentManager
.beginTransaction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class SelectActionFragment : Fragment() {
basicClusterBtn.setOnClickListener{ getCallback()?.handleBasicClicked() }
attestationTestBtn.setOnClickListener { getCallback()?.handleAttestationTestClicked() }
clusterInteractionBtn.setOnClickListener { getCallback()?.handleClusterInteractionClicked() }
provisionCustomFlowBtn.setOnClickListener{ getCallback()?.handleCustomFlowClicked() }
}
}

Expand Down Expand Up @@ -125,6 +126,8 @@ class SelectActionFragment : Fragment() {
fun onShowDeviceAddressInput()
/** Notifies listener of cluster interaction button click.. */
fun handleClusterInteractionClicked()
/** Notifies listener of provision-custom-flow button click. */
fun handleCustomFlowClicked()
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.google.chip.chiptool.R
import com.google.chip.chiptool.util.FragmentUtil
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.discoveryCapabilitiesTv
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.discriminatorTv
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.productIdTv
Expand All @@ -33,6 +34,8 @@ import kotlinx.android.synthetic.main.chip_device_info_fragment.view.vendorIdTv
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.vendorTagsContainer
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.vendorTagsLabelTv
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.versionTv
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.commissioningFlowTv
import kotlinx.android.synthetic.main.chip_device_info_fragment.view.customFlowBtn

/** Show the [CHIPDeviceInfo]. */
class CHIPDeviceDetailsFragment : Fragment() {
Expand Down Expand Up @@ -72,9 +75,26 @@ class CHIPDeviceDetailsFragment : Fragment() {
vendorTagsContainer.addView(tv)
}
}

commissioningFlowTv.text = "${deviceInfo.commissioningFlow}"

// commissioningFlow = 2 (Custom), read device info from Ledger
if (deviceInfo.commissioningFlow == 2) {
customFlowBtn.visibility = View.VISIBLE
customFlowBtn.setOnClickListener {
FragmentUtil.getHost(this@CHIPDeviceDetailsFragment, Callback::class.java)
?.handleReadFromLedgerClicked(deviceInfo)
}
}
}
}

/** Interface for notifying the host. */
interface Callback {
/** Notifies listener of Read Device Info from Ledger button click. */
fun handleReadFromLedgerClicked(deviceInfo: CHIPDeviceInfo)
}

companion object {
private const val ARG_DEVICE_INFO = "device_info"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ data class CHIPDeviceInfo(
val productId: Int = 0,
val discriminator: Int = 0,
val setupPinCode: Long = 0L,
var commissioningFlow: Int = 0,
val optionalQrCodeInfoMap: Map<Int, QrCodeInfo> = mapOf(),
val discoveryCapabilities: Set<DiscoveryCapability> = setOf(),
val ipAddress: String? = null,
) : Parcelable {

) : Parcelable {

companion object {
fun fromSetupPayload(setupPayload: SetupPayload): CHIPDeviceInfo {
Expand All @@ -44,6 +46,7 @@ data class CHIPDeviceInfo(
setupPayload.productId,
setupPayload.discriminator,
setupPayload.setupPinCode,
setupPayload.commissioningFlow,
setupPayload.optionalQRCodeInfo.mapValues { (_, info) ->
QrCodeInfo(
info.tag,
Expand All @@ -52,7 +55,7 @@ data class CHIPDeviceInfo(
info.int32
)
},
setupPayload.discoveryCapabilities
setupPayload.discoveryCapabilities,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package com.google.chip.chiptool.setuppayloadscanner

import android.net.Uri
import android.os.Bundle
import android.util.Base64
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.android.volley.Request
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.google.chip.chiptool.R
import com.google.chip.chiptool.util.FragmentUtil
import com.google.gson.Gson
import kotlinx.android.synthetic.main.chip_ledger_info_fragment.view.vendorIdTv
import kotlinx.android.synthetic.main.chip_ledger_info_fragment.view.productIdTv
import kotlinx.android.synthetic.main.chip_ledger_info_fragment.view.commissioningFlowUrlTv
import kotlinx.android.synthetic.main.chip_ledger_info_fragment.view.redirectBtn

/** Show the [CHIPDeviceInfo] from Ledger */
class CHIPLedgerDetailsFragment : Fragment() {

private lateinit var deviceInfo: CHIPDeviceInfo

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {

deviceInfo = checkNotNull(requireArguments().getParcelable(ARG_DEVICE_INFO))
val queue = Volley.newRequestQueue(context)
return inflater.inflate(R.layout.chip_ledger_info_fragment, container, false).apply {

// VID / PID
vendorIdTv.text = "${deviceInfo.vendorId}"
productIdTv.text = "${deviceInfo.productId}"

// Ledger api url
val url = Uri.parse(context.getString(R.string.dcl_api_root_url))
.buildUpon()
.appendPath("${deviceInfo.vendorId}")
.appendPath("${deviceInfo.productId}")
.build().toString()
Log.d(TAG, "Dcl request Url: $url")

// Ledger API call
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET, url, null,
{ response ->
Log.d(TAG, "Response from dcl $response")

// parse redirect Url
val responseJson = response.getJSONObject(context.getString(R.string.dcl_response_key))
val redirectUrl = responseJson.getString(context.getString(R.string.dcl_custom_flow_url_key))
Log.d(TAG, "Redirect Url from Ledger: $redirectUrl")
commissioningFlowUrlTv.text = redirectUrl

// generate redirect payload
val gson = Gson()
val payloadJson = gson.toJson(deviceInfo)
val payloadBase64 = Base64.encodeToString(payloadJson.toByteArray(), Base64.DEFAULT)
val redirectUrlWithPayload= Uri.parse(redirectUrl)
.buildUpon()
.appendQueryParameter("payload", payloadBase64)
.appendQueryParameter("returnUrl", context.getString(R.string.custom_flow_return_url))
.build()
.toString()

Log.d(TAG, "Redirect Url with Payload: $redirectUrlWithPayload")
redirectBtn.setOnClickListener {
FragmentUtil.getHost(this@CHIPLedgerDetailsFragment, Callback::class.java)
?.handleCustomFlowRedirectClicked(redirectUrlWithPayload)
}

// enable redirect button
redirectBtn.visibility = View.VISIBLE
},
{ error ->
Log.e(TAG, "Dcl request failed: $error")
commissioningFlowUrlTv.text = context.getString(R.string.chip_ledger_info_commissioning_flow_url_not_available)
}
)
queue.add(jsonObjectRequest)
}
}

/** Interface for notifying the host. */
interface Callback {
/** Notifies listener of Custom flow redirect button click. */
fun handleCustomFlowRedirectClicked(redirectUrl: String)
}

companion object {
private const val TAG = "CUSTOM_FLOW"
private const val ARG_DEVICE_INFO = "device_info"

@JvmStatic
fun newInstance(deviceInfo: CHIPDeviceInfo): CHIPLedgerDetailsFragment {
return CHIPLedgerDetailsFragment().apply {
arguments = Bundle(1).apply { putParcelable(ARG_DEVICE_INFO, deviceInfo) }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,37 @@
android:layout_below="@id/vendorTagsContainer"
android:layout_alignParentStart="true"
android:textSize="20sp"/>

<TextView
android:id="@+id/commissioningFlowLabelTv"
android:text="@string/chip_device_info_commissioning_flow_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_below="@id/discoveryCapabilitiesTv"
android:layout_alignParentStart="true"
android:textSize="20sp"/>
<TextView
android:id="@+id/commissioningFlowTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="8dp"
android:layout_below="@id/discoveryCapabilitiesTv"
android:layout_toEndOf="@id/commissioningFlowLabelTv"
android:layout_alignParentEnd="true"
android:textSize="20sp"/>

<Button
android:id="@+id/customFlowBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/commissioningFlowTv"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:visibility="invisible"
android:text="@string/chip_device_info_custom_flow_btn_text" />
</RelativeLayout>
</ScrollView>
Loading

0 comments on commit 3d7c25b

Please sign in to comment.