Set up BT (untested)

This commit is contained in:
Johannes Frohnmeyer 2023-01-25 17:04:39 +01:00
parent 6ebed9e77e
commit df5ab05dff
Signed by: Johannes
GPG Key ID: E76429612C2929F4
13 changed files with 254 additions and 151 deletions

View File

@ -5,10 +5,10 @@
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
@ -20,7 +20,7 @@
android:theme="@style/Theme.HC05AC"
tools:targetApi="31">
<activity
android:name=".DemoActivity"
android:name=".DeviceListActivity"
android:exported="true"
android:screenOrientation="landscape">
<intent-filter>
@ -29,7 +29,8 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".bt.LedControl"/>
<activity android:name=".BasicInputActivity"/>
<activity android:name=".BtInputActivity"/>
</application>
</manifest>

View File

@ -0,0 +1,40 @@
package io.gitlab.jfronny.hc05ac
import android.Manifest
import android.os.Build
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!hasPermission(Manifest.permission.ACCESS_NOTIFICATION_POLICY)) {
goToNotificationSettings()
}
if (!hasPermission(Manifest.permission.BLUETOOTH_ADMIN)) {
requestPermissions(Manifest.permission.BLUETOOTH_ADMIN)
}
if (!hasPermission(Manifest.permission.BLUETOOTH)) {
requestPermissions(Manifest.permission.BLUETOOTH)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !hasPermission(Manifest.permission.BLUETOOTH_CONNECT)) {
requestPermissions(Manifest.permission.BLUETOOTH_CONNECT)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !hasPermission(Manifest.permission.BLUETOOTH_SCAN)) {
requestPermissions(Manifest.permission.BLUETOOTH_SCAN)
}
if (!hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
requestPermissions(Manifest.permission.ACCESS_COARSE_LOCATION)
}
if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
requestPermissions(Manifest.permission.ACCESS_FINE_LOCATION)
}
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
)
}
}

View File

@ -0,0 +1,58 @@
package io.gitlab.jfronny.hc05ac
import android.bluetooth.BluetoothSocket
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import java.io.IOException
class BasicInputActivity : BaseActivity(), ConnectBtTask.Ctx {
override var address: String? = null
override fun close() = finish()
override val context: BasicInputActivity = this
override var btSocket: BluetoothSocket? = null
private var progress: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
address = intent.getStringExtra(DeviceListActivity.EXTRA_ADDRESS)
setContentView(R.layout.activity_led_control)
val btn1: Button = findViewById(R.id.button2)
val btn2: Button = findViewById(R.id.button3)
val btn3: Button = findViewById(R.id.button5)
val btn4: Button = findViewById(R.id.button6)
val btn5: Button = findViewById(R.id.button7)
val btnDis: Button = findViewById(R.id.button4)
progress = findViewById(R.id.textView2)
ConnectBtTask(this).execute()
btn1.setOnClickListener { sendSignal("1") }
btn2.setOnClickListener { sendSignal("2") }
btn3.setOnClickListener { sendSignal("3") }
btn4.setOnClickListener { sendSignal("4") }
btn5.setOnClickListener { sendSignal("5") }
btnDis.setOnClickListener { disconnect() }
}
private fun sendSignal(number: String) {
if (btSocket != null) {
try {
btSocket!!.outputStream.write(number.toByteArray())
} catch (e: IOException) {
logE("Could not send signal", e)
toast("Error")
}
}
}
private fun disconnect() {
if (btSocket != null) {
try {
btSocket!!.close()
} catch (e: IOException) {
logE("Could not disconnect", e)
toast("Error")
}
}
finish()
}
}

View File

@ -0,0 +1,40 @@
package io.gitlab.jfronny.hc05ac
import android.bluetooth.BluetoothSocket
import android.os.Bundle
import java.io.IOException
class BtInputActivity : InputActivity(), ConnectBtTask.Ctx {
override var address: String? = null
override fun close() = finish()
override val context: BtInputActivity = this
override var btSocket: BluetoothSocket? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
address = intent.getStringExtra(DeviceListActivity.EXTRA_ADDRESS)
ConnectBtTask(this).execute()
}
override fun send(left: Byte, right: Byte) {
if (btSocket != null) {
try {
btSocket!!.outputStream.write(byteArrayOf(left, right)) // byteArrayOf(Pack.pack(left, right))
} catch (e: IOException) {
logE("Could not send signal", e)
toast("Error")
}
}
}
override fun onDestroy() {
super.onDestroy()
if (btSocket != null) {
try {
btSocket!!.close()
} catch (e: IOException) {
logE("Could not disconnect", e)
}
}
}
}

View File

@ -0,0 +1,58 @@
package io.gitlab.jfronny.hc05ac
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ProgressDialog
import android.bluetooth.BluetoothSocket
import android.os.AsyncTask
import java.io.IOException
import java.util.*
class ConnectBtTask(private val ctx: Ctx) : AsyncTask<Void?, Void?, Void?>() {
private var connectSuccess = true
private var progress: ProgressDialog? = null
private var isBtConnected = false
override fun onPreExecute() {
progress = ProgressDialog.show(ctx.context, "Connecting...", "Please Wait!!!")
}
@SuppressLint("MissingPermission")
override fun doInBackground(vararg devices: Void?): Void? {
try {
if (ctx.btSocket == null || !isBtConnected) {
val bluetoothAdapter = ctx.context.btAdapter
val dispositivo = bluetoothAdapter.getRemoteDevice(ctx.address)
ctx.btSocket = dispositivo.createInsecureRfcommSocketToServiceRecord(appUUID)
bluetoothAdapter.cancelDiscovery()
ctx.btSocket!!.connect()
}
} catch (e: IOException) {
logE("Could not connect", e)
connectSuccess = false
}
return null
}
override fun onPostExecute(result: Void?) {
super.onPostExecute(result)
if (!connectSuccess) {
ctx.context.toast("Connection Failed. Is it a SPP Bluetooth? Try again.")
ctx.close()
} else {
ctx.context.toast("Connected")
isBtConnected = true
}
progress!!.dismiss()
}
interface Ctx {
val context: Activity
var btSocket: BluetoothSocket?
val address: String?
fun close()
}
companion object {
val appUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
}
}

View File

@ -1,20 +1,14 @@
package io.gitlab.jfronny.hc05ac.bt
package io.gitlab.jfronny.hc05ac
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.*
import android.widget.AdapterView.OnItemClickListener
import androidx.appcompat.app.AppCompatActivity
import io.gitlab.jfronny.hc05ac.R
import io.gitlab.jfronny.hc05ac.btAdapter
class DeviceList : AppCompatActivity() {
class DeviceListActivity : BaseActivity() {
private var deviceList: ListView? = null
private var bluetoothAdapter: BluetoothAdapter? = null
@SuppressLint("MissingPermission")
@ -25,8 +19,7 @@ class DeviceList : AppCompatActivity() {
deviceList = findViewById(R.id.listView)
bluetoothAdapter = btAdapter
if (bluetoothAdapter == null) {
Toast.makeText(applicationContext, "Bluetooth device not available", Toast.LENGTH_LONG)
.show()
toast("Bluetooth device not available")
finish()
} else if (!bluetoothAdapter!!.isEnabled) {
val turnBTon = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
@ -49,11 +42,7 @@ class DeviceList : AppCompatActivity() {
)
}
} else {
Toast.makeText(
applicationContext,
"No Paired Bluetooth Devices Found.",
Toast.LENGTH_LONG
).show()
toast("No Paired Bluetooth Devices Found.")
}
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, list)
deviceList!!.adapter = adapter
@ -64,13 +53,13 @@ class DeviceList : AppCompatActivity() {
OnItemClickListener { _: AdapterView<*>?, view: View, _: Int, _: Long ->
val info = (view as TextView).text.toString()
val address = info.substring(info.length - 17)
val i = Intent(this@DeviceList, LedControl::class.java)
// val i = Intent(this@DeviceListActivity, BasicInputActivity::class.java)
val i = Intent(this@DeviceListActivity, BtInputActivity::class.java)
i.putExtra(EXTRA_ADDRESS, address)
startActivity(i)
}
companion object {
@JvmField
var EXTRA_ADDRESS = "device_address"
}
}

View File

@ -1,31 +1,18 @@
package io.gitlab.jfronny.hc05ac
import android.Manifest
import android.os.Build
import android.os.Bundle
import android.view.MotionEvent
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.LinearLayoutCompat
abstract class InputActivity : AppCompatActivity() {
/**
* Activity that divides the screen into two halves, each serving as an input controller.
* Implementations define a send function which is called for every update.
*/
abstract class InputActivity : BaseActivity() {
private var root: LinearLayoutCompat? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!hasPermission(Manifest.permission.ACCESS_NOTIFICATION_POLICY)) {
goToNotificationSettings()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !hasPermission(Manifest.permission.BLUETOOTH_CONNECT)) {
requestPermissions(Manifest.permission.BLUETOOTH_CONNECT)
}
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
)
root = findViewById(R.id.root)
}

View File

@ -0,0 +1,19 @@
package io.gitlab.jfronny.hc05ac;
/**
* Utilities for packing bytes
* Uses bit-shifting, which is unsupported in kotlin (for good reason), so it is written in Java
*/
public class Pack {
public static byte pack(byte left, byte right) {
return (byte) ((left & 0xF0) | ((right & 0xF0) >> 4));
}
public static byte unpackLeft(byte packed) {
return (byte) (packed & 0xF0);
}
public static byte unpackRight(byte packed) {
return (byte) ((packed & 0x0F) << 4);
}
}

View File

@ -1,107 +0,0 @@
package io.gitlab.jfronny.hc05ac.bt
import android.annotation.SuppressLint
import android.app.ProgressDialog
import android.bluetooth.BluetoothSocket
import android.os.AsyncTask
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import io.gitlab.jfronny.hc05ac.R
import io.gitlab.jfronny.hc05ac.btAdapter
import java.io.IOException
import java.util.*
class LedControl : AppCompatActivity() {
var address: String? = null
var lumn: TextView? = null
private var progress: ProgressDialog? = null
var btSocket: BluetoothSocket? = null
private var isBtConnected = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val newint = intent
address = newint.getStringExtra(DeviceList.EXTRA_ADDRESS)
setContentView(R.layout.activity_led_control)
val btn1: Button = findViewById(R.id.button2)
val btn2: Button = findViewById(R.id.button3)
val btn3: Button = findViewById(R.id.button5)
val btn4: Button = findViewById(R.id.button6)
val btn5: Button = findViewById(R.id.button7)
val btnDis: Button = findViewById(R.id.button4)
lumn = findViewById(R.id.textView2)
ConnectBT().execute()
btn1.setOnClickListener { sendSignal("1") }
btn2.setOnClickListener { sendSignal("2") }
btn3.setOnClickListener { sendSignal("3") }
btn4.setOnClickListener { sendSignal("4") }
btn5.setOnClickListener { sendSignal("5") }
btnDis.setOnClickListener { disconnect() }
}
private fun sendSignal(number: String) {
if (btSocket != null) {
try {
btSocket!!.outputStream.write(number.toByteArray())
} catch (e: IOException) {
msg("Error")
}
}
}
private fun disconnect() {
if (btSocket != null) {
try {
btSocket!!.close()
} catch (e: IOException) {
msg("Error")
}
}
finish()
}
private fun msg(s: String) {
Toast.makeText(applicationContext, s, Toast.LENGTH_LONG).show()
}
private inner class ConnectBT : AsyncTask<Void?, Void?, Void?>() {
private var connectSuccess = true
override fun onPreExecute() {
progress = ProgressDialog.show(this@LedControl, "Connecting...", "Please Wait!!!")
}
@SuppressLint("MissingPermission")
override fun doInBackground(vararg devices: Void?): Void? {
try {
if (btSocket == null || !isBtConnected) {
val bluetoothAdapter = btAdapter
val dispositivo = bluetoothAdapter.getRemoteDevice(address)
btSocket = dispositivo.createInsecureRfcommSocketToServiceRecord(myUUID)
bluetoothAdapter.cancelDiscovery()
btSocket!!.connect()
}
} catch (e: IOException) {
connectSuccess = false
}
return null
}
override fun onPostExecute(result: Void?) {
super.onPostExecute(result)
if (!connectSuccess) {
msg("Connection Failed. Is it a SPP Bluetooth? Try again.")
finish()
} else {
msg("Connected")
isBtConnected = true
}
progress!!.dismiss()
}
}
companion object {
val myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
}
}

View File

@ -4,6 +4,7 @@ import android.app.Activity
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
@ -11,6 +12,7 @@ import android.os.Build
import android.provider.Settings
import android.util.Log
import android.view.MotionEvent
import android.widget.Toast
import androidx.core.app.ActivityCompat
import kotlin.math.max
import kotlin.math.min
@ -55,10 +57,14 @@ fun Float.clamp(min: Float, max: Float) = min(max(this, min), max)
fun logI(message: String) = Log.i(TAG, message)
fun logE(message: String, t: Throwable) = Log.e(TAG, message, t)
fun Context.hasPermission(permission: String) = ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
fun Activity.requestPermissions(vararg permissions: String, @androidx.annotation.IntRange(from = 0) requestCode: Int = 0)
= ActivityCompat.requestPermissions(this, permissions, requestCode)
val Activity.btAdapter get() = if (Build.VERSION.SDK_INT >= 31) (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
else BluetoothAdapter.getDefaultAdapter()
else BluetoothAdapter.getDefaultAdapter()
fun ContextWrapper.toast(msg: String) = Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG).show()

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".bt.DeviceList">
tools:context=".DeviceListActivity">
<LinearLayout
android:layout_width="match_parent"
@ -58,4 +58,4 @@
</LinearLayout>
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".bt.LedControl">
tools:context=".BasicInputActivity">
<LinearLayout
android:layout_width="match_parent"

View File

@ -10,4 +10,16 @@
app:divider="@drawable/divider"
app:showDividers="middle">
<View
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:background="?attr/colorPrimary" />
<View
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:background="?attr/colorSecondary" />
</androidx.appcompat.widget.LinearLayoutCompat>