practise BLE yes
This commit is contained in:
		| @@ -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 | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user