practise BLE yes
This commit is contained in:
parent
3e6fe310e0
commit
a9564bb441
@ -1,5 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
androidApplication
|
androidApplication
|
||||||
|
kotlinAndroid
|
||||||
}
|
}
|
||||||
|
|
||||||
val packageName = "ru.myitschool.work"
|
val packageName = "ru.myitschool.work"
|
||||||
@ -18,14 +19,18 @@ android {
|
|||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures.viewBinding = true
|
buildFeatures.dataBinding = true
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = Version.Kotlin.javaSource
|
sourceCompatibility = Version.Kotlin.javaSource
|
||||||
targetCompatibility = Version.Kotlin.javaSource
|
targetCompatibility = Version.Kotlin.javaSource
|
||||||
}
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation("androidx.core:core-ktx:1.7.0")
|
||||||
defaultLibrary()
|
defaultLibrary()
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,35 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<!-- Request legacy Bluetooth permissions on older devices. -->
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH"
|
||||||
|
android:maxSdkVersion="30" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
|
||||||
|
android:maxSdkVersion="30"/>
|
||||||
|
|
||||||
|
<!-- Needed only if your app looks for Bluetooth devices.
|
||||||
|
If your app doesn't use Bluetooth scan results to derive physical
|
||||||
|
location information, you can strongly assert that your app
|
||||||
|
doesn't derive physical location. -->
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
|
|
||||||
|
<!-- Needed only if your app makes the device discoverable to Bluetooth
|
||||||
|
devices. -->
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||||
|
|
||||||
|
<!-- Needed only if your app communicates with already-paired Bluetooth
|
||||||
|
devices. -->
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Needed only if your app uses Bluetooth scan results to derive physical location. -->
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
|
|
||||||
|
<uses-feature android:name="android.hardware.bluetooth" android:required="true"/>
|
||||||
|
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
@ -11,6 +40,21 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Default"
|
android:theme="@style/Theme.Default"
|
||||||
tools:targetApi="31" />
|
tools:targetApi="31" >
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:screenOrientation="portrait">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.lib_name"
|
||||||
|
android:value="" />
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
12
app/src/main/kotlin/ru/myitschool/work/ActivityViewModel.kt
Normal file
12
app/src/main/kotlin/ru/myitschool/work/ActivityViewModel.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package ru.myitschool.work
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class ActivityViewModel(
|
||||||
|
|
||||||
|
): ViewModel() {
|
||||||
|
val foundDevice = MutableLiveData<BluetoothDevice>();
|
||||||
|
val statemotors = MutableLiveData<String>("OFF");
|
||||||
|
}
|
126
app/src/main/kotlin/ru/myitschool/work/BluetoothScanner.kt
Normal file
126
app/src/main/kotlin/ru/myitschool/work/BluetoothScanner.kt
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package ru.myitschool.work
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.bluetooth.BluetoothAdapter
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
|
import android.bluetooth.BluetoothManager
|
||||||
|
import android.bluetooth.le.BluetoothLeScanner
|
||||||
|
import android.bluetooth.le.ScanCallback
|
||||||
|
import android.bluetooth.le.ScanFilter
|
||||||
|
import android.bluetooth.le.ScanResult
|
||||||
|
import android.bluetooth.le.ScanSettings
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
|
||||||
|
class BluetoothScanner(activityViewModel: ActivityViewModel, private val context: Context) {
|
||||||
|
private val mBluetoothAdapter: BluetoothAdapter
|
||||||
|
private val mBluetoothLeScanner: BluetoothLeScanner
|
||||||
|
private var mScanCallback: ScanCallback? = null
|
||||||
|
private val mHandler: Handler
|
||||||
|
private var mScanning = false
|
||||||
|
private var bluetoothDevice: BluetoothDevice? = null
|
||||||
|
private val activityViewModel: ActivityViewModel
|
||||||
|
|
||||||
|
init {
|
||||||
|
mHandler = Handler(Looper.getMainLooper())
|
||||||
|
val bluetoothManager =
|
||||||
|
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
context, Manifest.permission.BLUETOOTH_SCAN
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
mBluetoothAdapter = bluetoothManager.adapter
|
||||||
|
mBluetoothAdapter.startDiscovery()
|
||||||
|
mBluetoothLeScanner = mBluetoothAdapter.bluetoothLeScanner
|
||||||
|
this.activityViewModel = activityViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createScanSettings():ScanSettings{
|
||||||
|
TODO()//Реализовать метод создания ScanSetting с настройками Балансированного режима сканирования и немедленной показе результатов сканирования
|
||||||
|
return ScanSettings.Builder()
|
||||||
|
.setScanMode(ScanSettings.SCAN_MODE_BALANCED)
|
||||||
|
.setReportDelay(0)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
fun startScan(filters: ArrayList<ScanFilter>) {
|
||||||
|
if (!mScanning) {
|
||||||
|
Log.i(TAG, "Starting scan...")
|
||||||
|
val settings = createScanSettings()
|
||||||
|
mScanCallback = object : ScanCallback() {
|
||||||
|
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||||
|
super.onScanResult(callbackType, result)
|
||||||
|
val device = result.device
|
||||||
|
println("RESULT")
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
context, Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Found device: " + device.name + " (" + device.address + ")")
|
||||||
|
bluetoothDevice = device
|
||||||
|
activityViewModel.foundDevice.value = device
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBatchScanResults(results: List<ScanResult>) {
|
||||||
|
super.onBatchScanResults(results)
|
||||||
|
for (result in results) {
|
||||||
|
val device = result.device
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
context, Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Found device: " + device.name + " (" + device.address + ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onScanFailed(errorCode: Int) {
|
||||||
|
super.onScanFailed(errorCode)
|
||||||
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"Scan failed with error code: $errorCode"
|
||||||
|
)
|
||||||
|
Toast.makeText(context, "Scan failed with error code: ", Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mBluetoothLeScanner.startScan(filters, settings, mScanCallback)
|
||||||
|
mHandler.postDelayed({ stopScan() }, SCAN_PERIOD)
|
||||||
|
mScanning = true
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "Scan already in progress")
|
||||||
|
Toast.makeText(context, "Scan already in progress", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopScan() {
|
||||||
|
if (mScanning) {
|
||||||
|
Log.i(TAG, "Stopping scan...")
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
context, Manifest.permission.BLUETOOTH_SCAN
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mBluetoothLeScanner.stopScan(mScanCallback)
|
||||||
|
mScanning = false
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "No scan in progress")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = BluetoothScanner::class.java.simpleName
|
||||||
|
private const val SCAN_PERIOD: Long = 4000 // 4 seconds
|
||||||
|
}
|
||||||
|
}
|
389
app/src/main/kotlin/ru/myitschool/work/MainActivity.kt
Normal file
389
app/src/main/kotlin/ru/myitschool/work/MainActivity.kt
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
package ru.myitschool.work
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.bluetooth.*
|
||||||
|
import android.bluetooth.BluetoothDevice.*
|
||||||
|
import android.bluetooth.le.ScanFilter
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import ru.myitschool.work.databinding.ActivityMainBinding
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity() {
|
||||||
|
private val BLP_SERVICE_UUID: UUID? = UUID.fromString("e7112e6c-c396-11ed-afa1-0242ac120002");//e7112e6c-c396-11ed-afa1-0242ac120002
|
||||||
|
private val names:Array<String>? = arrayOf("RDB-1")
|
||||||
|
private val mac:Array<String>? = arrayOf("0C:B8:15:F6:0D:52")
|
||||||
|
|
||||||
|
|
||||||
|
val TAG ="CONTROLLER"
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var bluetoothGattCallback: BluetoothGattCallback
|
||||||
|
private lateinit var bluetoothScanner: BluetoothScanner
|
||||||
|
private lateinit var activityViewModel: ActivityViewModel
|
||||||
|
|
||||||
|
private lateinit var service:BluetoothGattService;
|
||||||
|
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
private var status:Int=-1;
|
||||||
|
private var newState:Int=-1;
|
||||||
|
private lateinit var gatt:BluetoothGatt;
|
||||||
|
private var isOffMotors=false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
activityViewModel = ViewModelProvider(this).get(
|
||||||
|
ActivityViewModel::class.java
|
||||||
|
)
|
||||||
|
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
|
||||||
|
|
||||||
|
binding.vm =activityViewModel;
|
||||||
|
binding.setLifecycleOwner(this);
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
if (allPermissionGranted()) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
askPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
|
bluetoothScanner = BluetoothScanner(activityViewModel,this);
|
||||||
|
|
||||||
|
val scanningButton = binding.Scan
|
||||||
|
scanningButton.setOnClickListener(View.OnClickListener {
|
||||||
|
Scanning()
|
||||||
|
})
|
||||||
|
val connectButton = binding.Connect
|
||||||
|
connectButton.setOnClickListener(View.OnClickListener {
|
||||||
|
Connect()
|
||||||
|
})
|
||||||
|
val OnOffMotorsButton = binding.OnOffMotors
|
||||||
|
OnOffMotorsButton.setOnClickListener(View.OnClickListener {
|
||||||
|
OnOffMotors()
|
||||||
|
})
|
||||||
|
val position1Button = binding.Position1
|
||||||
|
position1Button.setOnClickListener(View.OnClickListener {
|
||||||
|
Position1()
|
||||||
|
})
|
||||||
|
val position2Button = binding.Position2
|
||||||
|
position2Button.setOnClickListener(View.OnClickListener {
|
||||||
|
Position2()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
bluetoothGattCallback = object : BluetoothGattCallback(){
|
||||||
|
override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
|
||||||
|
super.onConnectionStateChange(gatt, status, newState)
|
||||||
|
Companion.newState = newState
|
||||||
|
Companion.status = status
|
||||||
|
if(newState==BluetoothProfile.STATE_CONNECTED)
|
||||||
|
Companion.gatt = gatt!!
|
||||||
|
|
||||||
|
}
|
||||||
|
override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
|
||||||
|
super.onServicesDiscovered(gatt, status)
|
||||||
|
|
||||||
|
for(ser in gatt!!.services){
|
||||||
|
println(" "+ ser.uuid+" "+ ser.characteristics.toString()+" "+ser.type+" "+ser.includedServices)
|
||||||
|
}
|
||||||
|
|
||||||
|
service = gatt!!.getService(BLP_SERVICE_UUID)
|
||||||
|
for(chara in service.characteristics) {
|
||||||
|
println(TAG + " " + chara.uuid + " " + chara.properties + " " + chara.permissions + " ")
|
||||||
|
}
|
||||||
|
val characteristic0 = service.getCharacteristic(service.characteristics[0].uuid)
|
||||||
|
Log.d(TAG,characteristic0.toString())
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
activityViewModel.statemotors.value = "Calibration"
|
||||||
|
}
|
||||||
|
gatt.readCharacteristic(service.characteristics[0])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCharacteristicRead(
|
||||||
|
gatt: BluetoothGatt?,
|
||||||
|
characteristic: BluetoothGattCharacteristic?,
|
||||||
|
status: Int
|
||||||
|
) {
|
||||||
|
super.onCharacteristicRead(gatt, characteristic, status)
|
||||||
|
|
||||||
|
if (status === BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
// Получаем данные характеристики
|
||||||
|
var value = characteristic!!.value.clone()
|
||||||
|
|
||||||
|
if(value[0]==(0).toByte() && value[1]==(0).toByte()) {//Если все двигатели выключены
|
||||||
|
isOffMotors = true;
|
||||||
|
//и включаем двигатели
|
||||||
|
value = byteArrayOf(0b11111111.toByte(), 0b00001111.toByte())
|
||||||
|
val characteristic0 = service.characteristics[0];
|
||||||
|
characteristic0.value = value!!
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
this@MainActivity,
|
||||||
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gatt!!.writeCharacteristic(characteristic0) // перезаписываем состояния двигателей чтобы их включить
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
Toast.makeText(this@MainActivity, "Двигатели уже включены!", Toast.LENGTH_SHORT).show()
|
||||||
|
activityViewModel.statemotors.value = "ON"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Делаем что-то с полученными данными
|
||||||
|
} else {
|
||||||
|
// Чтение характеристики не удалось
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCharacteristicWrite(
|
||||||
|
gatt: BluetoothGatt?,
|
||||||
|
characteristic: BluetoothGattCharacteristic?,
|
||||||
|
status: Int
|
||||||
|
) {
|
||||||
|
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||||
|
|
||||||
|
if (status === BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
if (characteristic == service.characteristics[0]){// данная характиристика отвечает за состояния вкл/выкл двигателя
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
activityViewModel.statemotors.value = "ON"
|
||||||
|
}
|
||||||
|
} else if (characteristic == service.characteristics[2]) { // данная характиристика отвечает за положение
|
||||||
|
|
||||||
|
println("VALUE "+Arrays.toString(characteristic!!.value))
|
||||||
|
val floats = FloatArray(characteristic.value.size/4);
|
||||||
|
var i = 0
|
||||||
|
while (i*4 < characteristic.value.size) {
|
||||||
|
val myFloat = ByteBuffer.wrap(characteristic.value).getFloat(i*4)
|
||||||
|
floats.set(i,myFloat)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
println("FLOAT "+Arrays.toString(floats))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
if (allPermissionGranted()) {
|
||||||
|
bluetoothScanner = BluetoothScanner(activityViewModel,this);
|
||||||
|
} else {
|
||||||
|
askPermissions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createScanFilter(name:String):ScanFilter{
|
||||||
|
TODO()//Реализовать createScanFilter - Создание scan filter по имени
|
||||||
|
return ScanFilter.Builder()
|
||||||
|
.setDeviceName(name)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
fun Scanning(){
|
||||||
|
println("Scanning")
|
||||||
|
var filters: ArrayList<ScanFilter> = ArrayList();
|
||||||
|
|
||||||
|
if (names != null) {
|
||||||
|
for (name in names) {
|
||||||
|
println(name)
|
||||||
|
val filter = createScanFilter(name)
|
||||||
|
filters.add(filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bluetoothScanner.startScan(filters)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun Connect(){
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||||
|
pairDevice(activityViewModel.foundDevice.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activityViewModel.foundDevice.value!=null) {
|
||||||
|
val gatt: BluetoothGatt = activityViewModel.foundDevice.value!!
|
||||||
|
.connectGatt(this, false, bluetoothGattCallback, TRANSPORT_AUTO)
|
||||||
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
gatt.readPhy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
fun OnOffMotors(){
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(this@MainActivity,Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
}
|
||||||
|
if(status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
// Мы подключились, можно запускать обнаружение сервисов
|
||||||
|
gatt?.discoverServices();
|
||||||
|
|
||||||
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||||
|
// Мы успешно отключились (контролируемое отключение)
|
||||||
|
gatt?.close();
|
||||||
|
} else {
|
||||||
|
// мы или подключаемся или отключаемся, просто игнорируем эти статусы
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Произошла ошибка... разбираемся, что случилось!
|
||||||
|
gatt?.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun floatAAToByteArray(floatAA: ArrayList<ArrayList<Float>>):ByteArray{
|
||||||
|
TODO()//Реализовать метод перевода из массива вещественных чисел к массиву байтов
|
||||||
|
var value :ByteArray? = null
|
||||||
|
value = ByteArray(floatAA.size*floatAA[0].size*Float.SIZE_BYTES)
|
||||||
|
val buffer = ByteBuffer.wrap(value)
|
||||||
|
for (i in floatAA.indices){
|
||||||
|
for(j in floatAA[0].indices){
|
||||||
|
buffer.putFloat(floatAA[i][j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Position1(){
|
||||||
|
val pos1 = arrayListOf<kotlin.collections.ArrayList<Float>>(arrayListOf(0.8f, 12f, 0f, 10f, 0f ))
|
||||||
|
val characteristic = service.getCharacteristic(service.characteristics[2].uuid)
|
||||||
|
Log.d(TAG,characteristic.uuid.toString())
|
||||||
|
var value :ByteArray? = floatAAToByteArray(pos1)
|
||||||
|
|
||||||
|
characteristic.value = value!!
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
this@MainActivity,
|
||||||
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gatt!!.writeCharacteristic(characteristic)
|
||||||
|
|
||||||
|
}
|
||||||
|
fun Position2(){
|
||||||
|
TODO()//Реализать метод Position2 для передачи массива вещественных чисел на (третью характеристику) [0.3, 2, 4.5, 2, 0.4]
|
||||||
|
val pos1 = arrayListOf<kotlin.collections.ArrayList<Float>>(arrayListOf(0.3f, 2f, 4.5f, 2f, 0.4f ))
|
||||||
|
val characteristic = service.getCharacteristic(service.characteristics[2].uuid)
|
||||||
|
Log.d(TAG,characteristic.uuid.toString())
|
||||||
|
var value :ByteArray? = floatAAToByteArray(pos1)
|
||||||
|
|
||||||
|
characteristic.value = value!!
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
this@MainActivity,
|
||||||
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gatt!!.writeCharacteristic(characteristic)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createPairingIntent(device:BluetoothDevice?):Intent{
|
||||||
|
TODO()//метод должен отправить intent для запроса присоединения к устройству с вводом пин кода
|
||||||
|
val intent = Intent(BluetoothDevice.ACTION_PAIRING_REQUEST)
|
||||||
|
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device)
|
||||||
|
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN)
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun pairDevice(device: BluetoothDevice?) {
|
||||||
|
val intent = createPairingIntent(device)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.BLUETOOTH_CONNECT
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val PERMISSIONS = arrayOf(Manifest.permission.BLUETOOTH_SCAN,Manifest.permission.BLUETOOTH_CONNECT)
|
||||||
|
|
||||||
|
private val requestLauncher = registerForActivityResult<Array<String>, Map<String, Boolean>>(
|
||||||
|
ActivityResultContracts.RequestMultiplePermissions()
|
||||||
|
) { permissions: Map<String, Boolean> ->
|
||||||
|
val permissionGranted =
|
||||||
|
AtomicBoolean(false)
|
||||||
|
permissions.forEach { (key: String, value: Boolean) ->
|
||||||
|
if (Arrays.asList(*PERMISSIONS).contains(key) && !value) {
|
||||||
|
Toast.makeText(this, "$key $value", Toast.LENGTH_SHORT).show()
|
||||||
|
permissionGranted.set(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!permissionGranted.get()) {
|
||||||
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
"Permission request denied",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
} else {
|
||||||
|
//do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun askPermissions() {
|
||||||
|
requestLauncher.launch(PERMISSIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allPermissionGranted(): Boolean {
|
||||||
|
val granted = AtomicBoolean(true)
|
||||||
|
Arrays.asList(*PERMISSIONS).forEach(Consumer { it: String? ->
|
||||||
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
it!!
|
||||||
|
) == PackageManager.PERMISSION_DENIED
|
||||||
|
) {
|
||||||
|
granted.set(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return granted.get()
|
||||||
|
}
|
||||||
|
}
|
88
app/src/main/res/layout/activity_main.xml
Normal file
88
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<layout>
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="vm"
|
||||||
|
type="ru.myitschool.work.ActivityViewModel" />
|
||||||
|
</data>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
<Button
|
||||||
|
android:id="@+id/Scan"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:text="Scan"
|
||||||
|
></Button>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:hint="DEVICE"
|
||||||
|
android:text="@{vm.foundDevice.name}"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
</TextView>
|
||||||
|
</LinearLayout>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/Connect"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:text="Connect"
|
||||||
|
></Button>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
<Button
|
||||||
|
android:id="@+id/OnOffMotors"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="OnOffMotors"
|
||||||
|
></Button>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:hint="state motors"
|
||||||
|
android:text="@{vm.statemotors}"
|
||||||
|
android:gravity="center">
|
||||||
|
</TextView>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/Position1"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="Position1"
|
||||||
|
></Button>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/Position2"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:text="Position2"
|
||||||
|
></Button>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
@ -2,4 +2,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
androidApplication version Version.gradle apply false
|
androidApplication version Version.gradle apply false
|
||||||
kotlinJvm version Version.Kotlin.language apply false
|
kotlinJvm version Version.Kotlin.language apply false
|
||||||
|
kotlinAndroid version Version.Kotlin.language apply false
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user