[go: nahoru, domu]

blob: 067ee6829c09bc691cadf0c50eba05d3bee1baa8 [file] [log] [blame]
/*
* Copyright 2023 The Android Open Source Project
*
* 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 androidx.bluetooth.integration.testapp.ui.gatt_server
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.EditText
import androidx.appcompat.app.AlertDialog
import androidx.bluetooth.BluetoothLe
import androidx.bluetooth.GattCharacteristic
import androidx.bluetooth.GattServerRequest
import androidx.bluetooth.GattService
import androidx.bluetooth.integration.testapp.R
import androidx.bluetooth.integration.testapp.databinding.FragmentGattServerBinding
import androidx.bluetooth.integration.testapp.ui.common.getColor
import androidx.bluetooth.integration.testapp.ui.common.setViewEditText
import androidx.bluetooth.integration.testapp.ui.common.toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@AndroidEntryPoint
class GattServerFragment : Fragment() {
private companion object {
private const val TAG = "GattServerFragment"
}
@Inject
lateinit var bluetoothLe: BluetoothLe
private val gattServerScope = CoroutineScope(Dispatchers.Main + Job())
private var gattServerJob: Job? = null
private var gattServerServicesAdapter: GattServerServicesAdapter? = null
private var isGattServerOpen: Boolean = false
set(value) {
field = value
if (value) {
_binding?.buttonGattServer?.text = getString(R.string.stop_gatt_server)
_binding?.buttonGattServer?.backgroundTintList = getColor(R.color.red_500)
} else {
_binding?.buttonGattServer?.text = getString(R.string.open_gatt_server)
_binding?.buttonGattServer?.backgroundTintList = getColor(R.color.indigo_500)
gattServerJob?.cancel()
gattServerJob = null
}
}
private val viewModel: GattServerViewModel by viewModels()
private var _binding: FragmentGattServerBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentGattServerBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.buttonAddService.setOnClickListener {
onAddGattService()
}
gattServerServicesAdapter =
GattServerServicesAdapter(
viewModel.gattServerServices,
::onAddGattCharacteristic
)
binding.recyclerViewGattServerServices.adapter = gattServerServicesAdapter
binding.recyclerViewGattServerServices.addItemDecoration(
DividerItemDecoration(context, LinearLayoutManager.VERTICAL)
)
binding.buttonGattServer.setOnClickListener {
if (gattServerJob?.isActive == true) {
isGattServerOpen = false
} else {
openGattServer()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
isGattServerOpen = false
_binding = null
}
private fun onAddGattService() {
Log.d(TAG, "onAddGattService() called")
val editTextUuid = EditText(requireActivity())
editTextUuid.hint = getString(R.string.service_uuid)
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.add_service))
.setViewEditText(editTextUuid)
.setPositiveButton(getString(R.string.add)) { _, _ ->
val editTextInput = editTextUuid.text.toString()
try {
val uuid = UUID.fromString(
when (editTextInput.length) {
4 -> "0000$editTextInput-0000-1000-8000-00805F9B34FB"
8 -> "$editTextInput-0000-1000-8000-00805F9B34FB"
else -> editTextInput
}
)
val service = GattService(uuid, listOf())
viewModel.addGattService(service)
gattServerServicesAdapter
?.notifyItemInserted(viewModel.gattServerServices.size - 1)
} catch (e: Exception) {
Log.d(TAG, e.toString())
toast(getString(R.string.invalid_uuid)).show()
}
}
.setNegativeButton(getString(R.string.cancel), null)
.create()
.show()
}
private fun onAddGattCharacteristic(bluetoothGattService: GattService) {
Log.d(
TAG, "onAddGattCharacteristic() called with: " +
"bluetoothGattService = $bluetoothGattService"
)
val view = layoutInflater.inflate(R.layout.dialog_add_characteristic, null)
val editTextUuid = view.findViewById<EditText>(R.id.edit_text_uuid)
val checkBoxPropertiesBroadcast =
view.findViewById<CheckBox>(R.id.check_box_properties_broadcast)
val checkBoxPropertiesIndicate =
view.findViewById<CheckBox>(R.id.check_box_properties_indicate)
val checkBoxPropertiesNotify = view.findViewById<CheckBox>(R.id.check_box_properties_notify)
val checkBoxPropertiesRead = view.findViewById<CheckBox>(R.id.check_box_properties_read)
val checkBoxPropertiesSignedWrite =
view.findViewById<CheckBox>(R.id.check_box_properties_signed_write)
val checkBoxPropertiesWrite = view.findViewById<CheckBox>(R.id.check_box_properties_write)
val checkBoxPropertiesWriteNoResponse =
view.findViewById<CheckBox>(R.id.check_box_properties_write_no_response)
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.add_characteristic))
.setView(view)
.setPositiveButton(getString(R.string.add)) { _, _ ->
val uuidText = editTextUuid.text.toString()
var properties = 0
if (checkBoxPropertiesBroadcast.isChecked) {
properties = properties or GattCharacteristic.PROPERTY_BROADCAST
}
if (checkBoxPropertiesIndicate.isChecked) {
properties = properties or GattCharacteristic.PROPERTY_INDICATE
}
if (checkBoxPropertiesNotify.isChecked) {
properties = properties or GattCharacteristic.PROPERTY_NOTIFY
}
if (checkBoxPropertiesRead.isChecked) {
properties = properties or GattCharacteristic.PROPERTY_READ
}
if (checkBoxPropertiesSignedWrite.isChecked) {
properties = properties or GattCharacteristic.PROPERTY_SIGNED_WRITE
}
if (checkBoxPropertiesWrite.isChecked) {
properties = properties or GattCharacteristic.PROPERTY_WRITE
}
if (checkBoxPropertiesWriteNoResponse.isChecked) {
properties = properties or GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
}
try {
val uuid = UUID.fromString(
when (uuidText.length) {
4 -> "0000$uuidText-0000-1000-8000-00805F9B34FB"
8 -> "$uuidText-0000-1000-8000-00805F9B34FB"
else -> uuidText
}
)
val sampleCharacteristic = GattCharacteristic(uuid, properties)
val index = viewModel.gattServerServices.indexOf(bluetoothGattService)
viewModel.addGattCharacteristic(bluetoothGattService, sampleCharacteristic)
gattServerServicesAdapter?.notifyItemChanged(index)
} catch (e: Exception) {
toast(getString(R.string.invalid_uuid)).show()
}
}
.setNegativeButton(getString(R.string.cancel), null)
.create()
.show()
}
private fun openGattServer() {
Log.d(TAG, "openGattServer() called")
gattServerJob = gattServerScope.launch {
Log.d(
TAG, "bluetoothLe.openGattServer() called with: " +
"viewModel.gattServerServices = ${viewModel.gattServerServices}"
)
isGattServerOpen = true
bluetoothLe.openGattServer(viewModel.gattServerServices) {
Log.d(
TAG, "bluetoothLe.openGattServer() called with: " +
"viewModel.gattServerServices = ${viewModel.gattServerServices}"
)
connectRequests.collect {
Log.d(TAG, "connectRequests.collected: GattServerConnectRequest = $it")
launch {
it.accept {
Log.d(
TAG, "GattServerConnectRequest accepted: " +
"GattServerSessionScope = $it"
)
requests.collect { gattServerRequest ->
Log.d(
TAG, "requests collected: " +
"gattServerRequest = $gattServerRequest"
)
// TODO(b/269390098): Handle requests correctly
when (gattServerRequest) {
is GattServerRequest.ReadCharacteristic -> {
val characteristic = gattServerRequest.characteristic
val value = viewModel.readGattCharacteristicValue(
characteristic
)
toast(
"Read value: ${value.decodeToString()} " +
"for characteristic = ${characteristic.uuid}"
).show()
gattServerRequest.sendResponse(value)
}
is GattServerRequest.WriteCharacteristics -> {
val characteristic =
gattServerRequest.parts[0].characteristic
val value = gattServerRequest.parts[0].value
toast(
"Writing value: ${value.decodeToString()} " +
"to characteristic = ${characteristic.uuid}"
).show()
viewModel.updateGattCharacteristicValue(
characteristic,
value
)
gattServerRequest.sendResponse()
}
else -> {
throw NotImplementedError("Unknown request")
}
}
}
}
}
}
}
}
}
}