2 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 package com.google.chip.chiptool.provisioning
21 import android.bluetooth.BluetoothGatt
22 import android.os.Bundle
23 import android.util.Log
24 import android.view.LayoutInflater
25 import android.view.View
26 import android.view.ViewGroup
27 import android.widget.Toast
28 import androidx.fragment.app.Fragment
29 import com.google.chip.chiptool.ChipClient
30 import com.google.chip.chiptool.GenericChipDeviceListener
31 import com.google.chip.chiptool.R
32 import com.google.chip.chiptool.bluetooth.BluetoothManager
33 import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo
34 import com.google.chip.chiptool.util.DeviceIdUtil
35 import com.google.chip.chiptool.util.FragmentUtil
36 import kotlinx.coroutines.CoroutineScope
37 import kotlinx.coroutines.Dispatchers
38 import kotlinx.coroutines.ExperimentalCoroutinesApi
39 import kotlinx.coroutines.Job
40 import kotlinx.coroutines.cancel
41 import kotlinx.coroutines.launch
43 @ExperimentalCoroutinesApi
44 class DeviceProvisioningFragment : Fragment() {
46 private lateinit var deviceInfo: CHIPDeviceInfo
48 private var gatt: BluetoothGatt? = null
50 private val networkType: ProvisionNetworkType
51 get() = requireNotNull(
52 ProvisionNetworkType.fromName(arguments?.getString(ARG_PROVISION_NETWORK_TYPE))
55 private val scope = CoroutineScope(Dispatchers.Main + Job())
57 override fun onCreateView(
58 inflater: LayoutInflater,
59 container: ViewGroup?,
60 savedInstanceState: Bundle?
62 deviceInfo = checkNotNull(requireArguments().getParcelable(ARG_DEVICE_INFO))
63 return inflater.inflate(R.layout.single_fragment_container, container, false).apply {
64 if (savedInstanceState == null) {
65 startConnectingToDevice()
70 override fun onStop() {
76 private fun startConnectingToDevice() {
82 val deviceController = ChipClient.getDeviceController()
83 val bluetoothManager = BluetoothManager()
86 R.string.rendezvous_over_ble_scanning_text,
87 deviceInfo.discriminator.toString()
89 val device = bluetoothManager.getBluetoothDevice(deviceInfo.discriminator) ?: run {
90 showMessage(R.string.rendezvous_over_ble_scanning_failed_text)
95 R.string.rendezvous_over_ble_connecting_text,
96 device.name ?: device.address.toString()
98 gatt = bluetoothManager.connect(requireContext(), device)
100 showMessage(R.string.rendezvous_over_ble_pairing_text)
101 deviceController.setCompletionListener(ConnectionCallback())
103 val deviceId = DeviceIdUtil.getNextAvailableId(requireContext())
104 deviceController.pairDevice(gatt, deviceId, deviceInfo.setupPinCode)
105 DeviceIdUtil.setNextAvailableId(requireContext(), deviceId + 1)
109 private fun showMessage(msgResId: Int, stringArgs: String? = null) {
110 requireActivity().runOnUiThread {
111 val context = requireContext()
112 Toast.makeText(context, context.getString(msgResId, stringArgs), Toast.LENGTH_SHORT)
117 inner class ConnectionCallback : GenericChipDeviceListener() {
118 override fun onConnectDeviceComplete() {
119 Log.d(TAG, "onConnectDeviceComplete")
122 override fun onStatusUpdate(status: Int) {
123 Log.i(TAG, "Pairing status update: $status");
125 if (status == STATUS_NETWORK_PROVISIONING_SUCCESS) {
126 showMessage(R.string.rendezvous_over_ble_provisioning_success_text)
127 FragmentUtil.getHost(this@DeviceProvisioningFragment, Callback::class.java)
128 ?.onPairingComplete()
132 override fun onNetworkCredentialsRequested() {
133 childFragmentManager.beginTransaction()
134 .add(R.id.fragment_container, EnterNetworkFragment.newInstance(networkType))
138 override fun onPairingComplete(code: Int) {
139 Log.d(TAG, "onPairingComplete: $code")
142 override fun onPairingDeleted(code: Int) {
143 Log.d(TAG, "onPairingDeleted: $code")
146 override fun onCloseBleComplete() {
147 Log.d(TAG, "onCloseBleComplete")
150 override fun onError(error: Throwable?) {
151 Log.d(TAG, "onError: $error")
155 /** Callback from [DeviceProvisioningFragment] notifying any registered listeners. */
157 /** Notifies that pairing has been completed. */
158 fun onPairingComplete()
162 private const val TAG = "DeviceProvisioningFragment"
163 private const val ARG_DEVICE_INFO = "device_info"
164 private const val ARG_PROVISION_NETWORK_TYPE = "provision_network_type"
165 private const val STATUS_NETWORK_PROVISIONING_SUCCESS = 2
168 deviceInfo: CHIPDeviceInfo,
169 networkType: ProvisionNetworkType
170 ): DeviceProvisioningFragment {
171 return DeviceProvisioningFragment().apply {
172 arguments = Bundle(2).apply {
173 putParcelable(ARG_DEVICE_INFO, deviceInfo)
174 putString(ARG_PROVISION_NETWORK_TYPE, networkType.name)