Bluetooth printer scanning using Kotlin
One of the requirements of my current project is to be able to establish Bluetooth connection and send texts to print.
See the video below for the result.
Let's create an empty Android project and let's call it BluetoothPrintingSample. Then, create a layout folder inside res folder. Let's create a simple layout called activity_print.xml.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingStart="44dp" android:paddingEnd="44dp" android:background="@color/design_default_color_secondary" > <EditText android:id="@+id/editTextPrintMessage" android:layout_width="match_parent" android:layout_height="200dp" android:singleLine="false" android:background="@android:color/white" android:textColor="@android:color/black" android:layout_marginTop="30dp" android:layout_centerHorizontal="true" /> <Button android:id="@+id/buttonPrintMe" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Print Me" android:backgroundTint="@color/teal_700" android:layout_below="@+id/editTextPrintMessage" /> </RelativeLayout>
Open your build.gradle (app) file and enable the ViewBinding feature.
buildFeatures { viewBinding true }
Let's create our activity. It should look like the one below.
package me.lwgmnz.bluetoothprintingsample import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import me.lwgmnz.bluetoothprintingsample.databinding.ActivityPrintBinding class PrintActivity : AppCompatActivity() { private lateinit var binding: ActivityPrintBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityPrintBinding.inflate(layoutInflater) val view = binding.root setContentView(view) } }
Now the permissions. Open your AndroidManifest.xml and add the following permissions below. Take note, to include the permission ACCESS_COARSE_LOCATION.
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
In your PrintActivity, add the runtime permission.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), COARSE_LOCATION_REQUEST_CODE) }
For newer devices, you also might need to add the runtime Bluetooth permission. But for my current project the app is running on Android 8.1 and Bluetooth permissions will work fine when added only on AndroidManifest.xml.
If Bluetooth is not yet enabled, we should enable it.
private var bluetoothAdapter: BluetoothAdapter? = null override fun onCreate() { super.onCreate() bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() if (bluetoothAdapter != null) { if (bluetoothAdapter!!.isEnabled) { bluetoothAdapter!!.startDiscovery() // Listen for broadcast val filter = IntentFilter(BluetoothDevice.ACTION_FOUND) registerReceiver(bluetoothScanBroadcastReceiver, filter) } else { // Enable Bluetooth val intentBluetooth = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) startActivityForResult(intentBluetooth, Keys.BLUETOOTH_REQUEST_CODE) } } else { Snackbar.make(binding.root, "Bluetooth Not Supported", Snackbar.LENGTH_SHORT).show() } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == Keys.BLUETOOTH_REQUEST_CODE) { if (resultCode == RESULT_OK) { initBluetooth() } } super.onActivityResult(requestCode, resultCode, data) }
Add a BroadcastReceiver so we can receive whenever our app discovers Bluetooth devices when scanning. The device name of my Bluetooth printer is Bluetooth Printer so as you can see I purposely check for the specific name to save resources.
Also take note of the UUID, you should use that specific UUID as UUID.randomString()
won't work.
private var bluetoothSocket: BluetoothSocket? = null private var bluetoothAdapter: BluetoothAdapter? = null private var outputStream: OutputStream? = null private val bluetoothScanBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == BluetoothDevice.ACTION_FOUND) { val bluetoothDevice = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE) if (bluetoothDevice != null) { if (bluetoothDevice.name == "BlueTooth Printer") { val uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid) bluetoothAdapter!!.cancelDiscovery() try { bluetoothSocket!!.connect() outputStream = bluetoothSocket!!.outputStream } catch (exception: IOException) { Log.e("SignInActivity", "IOException: $exception") } } } } } }
Add our BroadcastReceiver to its proper Activity lifecycles.
override fun onResume() { super.onResume() val filter = IntentFilter(BluetoothDevice.ACTION_FOUND) registerReceiver(bluetoothScanBroadcastReceiver, filter) } override fun onPause() { super.onPause() unregisterReceiver(bluetoothScanBroadcastReceiver) }
Time to print. Back to our PrintActivity let's add the lines below
override fun onCreate() { super.onCreate() binding.buttonPrintMe.setOnClickListener(this) } override fun onClick(view: View) { if (view.id == binding.buttonPrintMe.id) { if (outputStream != null) { outputStream!!.write(binding.editTextPrintMessage.text.toString().toByteArray()) } } }
It should print out the words you entered on the EditText.
That's it! Let me know if you have any questions. You can email me at yo@lwgmnz.me and let me know what you think.