blob: 7797aef19fa2339400da5adf49ae9873820f6c81 [file] [log] [blame]
/*
* Copyright (c) 2020 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
import android.content.Intent
import android.net.Uri
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.os.Bundle
import android.util.Base64
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chip.setuppayload.SetupPayload
import chip.setuppayload.SetupPayloadParser
import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException
import com.google.chip.chiptool.provisioning.AddressCommissioningFragment
import com.google.chip.chiptool.provisioning.DeviceProvisioningFragment
import com.google.chip.chiptool.provisioning.EnterNetworkFragment
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
import org.json.JSONObject
class CHIPToolActivity :
AppCompatActivity(),
BarcodeFragment.Callback,
SelectActionFragment.Callback,
DeviceProvisioningFragment.Callback,
EnterNetworkFragment.Callback,
CHIPDeviceDetailsFragment.Callback,
CHIPLedgerDetailsFragment.Callback {
private var networkType: ProvisionNetworkType? = null
private var deviceInfo: CHIPDeviceInfo? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.top_activity)
if (savedInstanceState == null) {
val fragment = SelectActionFragment.newInstance()
supportFragmentManager
.beginTransaction()
.add(R.id.nav_host_fragment, fragment, fragment.javaClass.simpleName)
.commit()
} else {
networkType =
ProvisionNetworkType.fromName(savedInstanceState.getString(ARG_PROVISION_NETWORK_TYPE))
}
if (intent?.action == NfcAdapter.ACTION_NDEF_DISCOVERED)
onNfcIntent(intent)
if (Intent.ACTION_VIEW == intent?.action) {
onReturnIntent(intent)
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putString(ARG_PROVISION_NETWORK_TYPE, networkType?.name)
super.onSaveInstanceState(outState)
}
override fun onCHIPDeviceInfoReceived(deviceInfo: CHIPDeviceInfo) {
this.deviceInfo = deviceInfo
if (networkType == null) {
showFragment(CHIPDeviceDetailsFragment.newInstance(deviceInfo))
} else {
if (deviceInfo.ipAddress != null) {
showFragment(DeviceProvisioningFragment.newInstance(deviceInfo!!, null))
} else {
showFragment(EnterNetworkFragment.newInstance(networkType!!), false)
}
}
}
override fun onCommissioningComplete(code: Int) {
runOnUiThread {
Toast.makeText(
this,
getString(R.string.commissioning_completed, code),
Toast.LENGTH_SHORT).show()
}
ChipClient.getDeviceController(this).close()
showFragment(SelectActionFragment.newInstance(), false)
}
override fun onShowDeviceAddressInput() {
showFragment(AddressCommissioningFragment.newInstance(), false)
}
override fun onNetworkCredentialsEntered(networkCredentials: NetworkCredentialsParcelable) {
showFragment(DeviceProvisioningFragment.newInstance(deviceInfo!!, networkCredentials))
}
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 SetNetworkType(type: ProvisionNetworkType) {
networkType = type
}
private fun showFragment(fragment: Fragment, showOnBack: Boolean = true) {
val fragmentTransaction = supportFragmentManager
.beginTransaction()
.replace(R.id.nav_host_fragment, fragment, fragment.javaClass.simpleName)
if (showOnBack) {
fragmentTransaction.addToBackStack(null)
}
fragmentTransaction.commit()
}
private fun onNfcIntent(intent: Intent?) {
// Require 1 NDEF message containing 1 NDEF record
val messages = intent?.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
if (messages?.size != 1) return
val records = (messages[0] as NdefMessage).records
if (records.size != 1) return
// Require NDEF URI record starting with "mt:"
val uri = records[0].toUri()
if (!uri?.scheme.equals("mt", true)) return
lateinit var setupPayload: SetupPayload
try {
setupPayload =
SetupPayloadParser().parseQrCode(uri.toString().toUpperCase())
} catch (ex: UnrecognizedQrCodeException) {
Log.e(TAG, "Unrecognized QR Code", ex)
Toast.makeText(this, "Unrecognized QR Code", Toast.LENGTH_SHORT).show()
return
}
val deviceInfo = CHIPDeviceInfo.fromSetupPayload(setupPayload)
val buttons = arrayOf(
getString(R.string.nfc_tag_action_show),
getString(R.string.nfc_tag_action_wifi),
getString(R.string.nfc_tag_action_thread))
AlertDialog.Builder(this)
.setTitle(R.string.nfc_tag_action_title)
.setItems(buttons) { _, which ->
this.networkType = when (which) {
1 -> ProvisionNetworkType.WIFI
2 -> ProvisionNetworkType.THREAD
else -> null
}
onCHIPDeviceInfoReceived(deviceInfo)
}
.create()
.show()
}
private fun onReturnIntent(intent: Intent) {
val appLinkData = intent.data
// Require URI schema "mt:"
if (!appLinkData?.scheme.equals("mt", true)) {
Log.d(TAG, "Unrecognized URI schema : ${appLinkData?.scheme}")
return
}
// Require URI host "modelinfo"
if (!appLinkData?.host.equals("modelinfo", true)) {
Log.d(TAG, "Unrecognized URI host : ${appLinkData?.host}")
return
}
// parse payload
try {
val payloadBase64String = appLinkData?.getQueryParameter("payload")
if (payloadBase64String.isNullOrEmpty()) {
Log.d(TAG, "Unrecognized payload")
return
}
val decodeBytes = Base64.decode(payloadBase64String, Base64.DEFAULT)
val payloadString = String(decodeBytes)
val payload = JSONObject(payloadString)
// parse payload from JSON
val setupPayload = SetupPayload()
// set defaults
setupPayload.discoveryCapabilities = setOf()
setupPayload.optionalQRCodeInfo = mapOf()
// read from payload
setupPayload.version = payload.getInt("version")
setupPayload.vendorId = payload.getInt("vendorId")
setupPayload.productId = payload.getInt("productId")
setupPayload.commissioningFlow = payload.getInt("commissioningFlow")
setupPayload.discriminator = payload.getInt("discriminator")
setupPayload.setupPinCode = payload.getLong("setupPinCode")
val deviceInfo = CHIPDeviceInfo.fromSetupPayload(setupPayload)
val buttons = arrayOf(
getString(R.string.nfc_tag_action_show)
)
AlertDialog.Builder(this)
.setTitle(R.string.provision_custom_flow_alert_title)
.setItems(buttons) { _, _ ->
onCHIPDeviceInfoReceived(deviceInfo)
}
.create()
.show()
} catch (ex: UnrecognizedQrCodeException) {
Log.e(TAG, "Unrecognized Payload", ex)
Toast.makeText(this, "Unrecognized Setup Payload", Toast.LENGTH_SHORT).show()
return
}
}
companion object {
private const val TAG = "CHIPToolActivity"
private const val ADDRESS_COMMISSIONING_FRAGMENT_TAG = "address_commissioning_fragment"
private const val ARG_PROVISION_NETWORK_TYPE = "provision_network_type"
}
}