From 51240a707292b6267921be1a6148ea330cf12123 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Mon, 1 Jun 2026 15:58:46 +0100 Subject: [PATCH 01/21] fix(bluetoothle): fix typo in BluetoothLeService and migrate to snippets - Fix typo 'Return;' -> 'return;' in Java BluetoothLeService. - Migrate inline code samples from connect-gatt-server.md to snippets. - Create BluetoothLeService and DeviceControlActivity in Java and Kotlin. - Add gatt_services_characteristics layout and string resources to enable compilation. Bug: 359501045 --- .../bluetoothle/java/BluetoothLeService.java | 172 ++++++++++++++++++ .../java/DeviceControlActivity.java | 172 ++++++++++++++++++ .../bluetoothle/kotlin/BluetoothLeService.kt | 167 +++++++++++++++++ .../kotlin/DeviceControlActivity.kt | 171 +++++++++++++++++ .../layout/gatt_services_characteristics.xml | 21 +++ bluetoothle/src/main/res/values/strings.xml | 5 +- 6 files changed, 707 insertions(+), 1 deletion(-) create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt create mode 100644 bluetoothle/src/main/res/layout/gatt_services_characteristics.xml diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java new file mode 100644 index 000000000..40bad9204 --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -0,0 +1,172 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.java; + +import android.Manifest; +import android.app.Service; +import androidx.annotation.RequiresPermission; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothProfile; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; +import androidx.annotation.Nullable; + +// [START android_bluetooth_service_all_java] +public class BluetoothLeService extends Service { + + public static final String TAG = "BluetoothLeService"; + + private final Binder binder = new LocalBinder(); + private BluetoothAdapter bluetoothAdapter; + private BluetoothGatt bluetoothGatt; + private int connectionState; + + // [START android_bluetooth_constants_java] + public static final String ACTION_GATT_CONNECTED = + "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; + public static final String ACTION_GATT_DISCONNECTED = + "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; + + private static final int STATE_DISCONNECTED = 0; + private static final int STATE_CONNECTED = 2; + // [END android_bluetooth_constants_java] + + + // [START android_bluetooth_binder_java] + @Nullable + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + public class LocalBinder extends Binder { + public BluetoothLeService getService() { + return BluetoothLeService.this; + } + } + // [END android_bluetooth_binder_java] + + // [START android_bluetooth_initialize_java] + public boolean initialize() { + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (bluetoothAdapter == null) { + Log.e(TAG, "Unable to obtain a BluetoothAdapter."); + return false; + } + return true; + } + // [END android_bluetooth_initialize_java] + + // [START android_bluetooth_connect_simple_java] + public boolean simpleConnect(final String address) { + if (bluetoothAdapter == null || address == null) { + Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); + return false; + } + try { + final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); + } catch (IllegalArgumentException exception) { + Log.w(TAG, "Device not found with provided address."); + return false; + } + // connect to the GATT server on the device + return true; + } + // [END android_bluetooth_connect_simple_java] + + // [START android_bluetooth_connect_java] + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + public boolean connect(final String address) { + if (bluetoothAdapter == null || address == null) { + Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); + return false; + } + try { + final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); + // connect to the GATT server on the device + bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback); + return true; + } catch (IllegalArgumentException exception) { + Log.w(TAG, "Device not found with provided address. Unable to connect."); + return false; + } + } + // [END android_bluetooth_connect_java] + + // [START android_bluetooth_callback_simple_java] + private final BluetoothGattCallback simpleBluetoothGattCallback = new BluetoothGattCallback() { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + } + } + }; + // [END android_bluetooth_callback_simple_java] + + // [START android_bluetooth_callback_java] + private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + connectionState = STATE_CONNECTED; + broadcastUpdate(ACTION_GATT_CONNECTED); + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + connectionState = STATE_DISCONNECTED; + broadcastUpdate(ACTION_GATT_DISCONNECTED); + } + } + }; + // [END android_bluetooth_callback_java] + + // [START android_bluetooth_broadcast_java] + private void broadcastUpdate(final String action) { + final Intent intent = new Intent(action); + sendBroadcast(intent); + } + // [END android_bluetooth_broadcast_java] + + // [START android_bluetooth_close_java] + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + @Override + public boolean onUnbind(Intent intent) { + close(); + return super.onUnbind(intent); + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + private void close() { + if (bluetoothGatt == null) { + return; // FIXED: lowercase 'return' + } + bluetoothGatt.close(); + bluetoothGatt = null; + } + // [END android_bluetooth_close_java] +} + +// [END android_bluetooth_service_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java new file mode 100644 index 000000000..92ddae9d1 --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -0,0 +1,172 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.java; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import android.Manifest; +import com.sample.android.bluetoothle.R; +import com.sample.android.bluetoothle.java.BluetoothLeService.LocalBinder; +import androidx.annotation.RequiresPermission; + + + +// [START android_bluetooth_activity_all_java] +public class DeviceControlActivity extends AppCompatActivity { + + private static final String TAG = "DeviceControlActivity"; + + private BluetoothLeService bluetoothService; + private String deviceAddress; + private boolean connected = false; + + // [START android_bluetooth_service_connection_simple_java] + private final ServiceConnection simpleServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + bluetoothService = ((LocalBinder) service).getService(); + if (bluetoothService != null) { + // call functions on service to check connection and connect to devices + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + bluetoothService = null; + } + }; + // [END android_bluetooth_service_connection_simple_java] + + // [START android_bluetooth_service_connection_initialize_java] + private final ServiceConnection initializeServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + bluetoothService = ((LocalBinder) service).getService(); + if (bluetoothService != null) { + if (!bluetoothService.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth"); + finish(); + } + // perform device connection + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + bluetoothService = null; + } + }; + // [END android_bluetooth_service_connection_initialize_java] + + // [START android_bluetooth_service_connection_java] + + private final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + public void onServiceConnected(ComponentName name, IBinder service) { + bluetoothService = ((LocalBinder) service).getService(); + if (bluetoothService != null) { + // [START android_bluetooth_initialize_activity_java] + if (!bluetoothService.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth"); + finish(); + } + // [END android_bluetooth_initialize_activity_java] + // perform device connection + // [START android_bluetooth_connect_activity_java] + bluetoothService.connect(deviceAddress); + // [END android_bluetooth_connect_activity_java] + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + bluetoothService = null; + } + }; + // [END android_bluetooth_service_connection_java] + + // [START android_bluetooth_update_receiver_java] + private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { + connected = true; + updateConnectionState(R.string.connected); + } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { + connected = false; + updateConnectionState(R.string.disconnected); + } + } + }; + // [END android_bluetooth_update_receiver_java] + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.gatt_services_characteristics); + + deviceAddress = getIntent().getStringExtra("EXTRAS_DEVICE_ADDRESS"); + + // [START android_bluetooth_bind_service_java] + Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); + bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); + // [END android_bluetooth_bind_service_java] + } + + // [START android_bluetooth_receiver_lifecycle_java] + @Override + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + protected void onResume() { + super.onResume(); + + registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()); + if (bluetoothService != null) { + final boolean result = bluetoothService.connect(deviceAddress); + Log.d(TAG, "Connect request result=" + result); + } + } + + @Override + protected void onPause() { + super.onPause(); + unregisterReceiver(gattUpdateReceiver); + } + private static IntentFilter makeGattUpdateIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); + return intentFilter; + } + // [END android_bluetooth_receiver_lifecycle_java] + + + private void updateConnectionState(int resourceId) { + // Dummy implementation + } +} +// [END android_bluetooth_activity_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt new file mode 100644 index 000000000..cc4b237bc --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.kotlin + +import android.Manifest +import android.app.Service +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCallback +import android.bluetooth.BluetoothProfile +import android.content.Intent +import android.os.Binder +import android.os.IBinder +import android.util.Log +import androidx.annotation.RequiresPermission + +private const val TAG = "BluetoothLeService" + +// [START android_bluetooth_service_all] +class BluetoothLeService : Service() { + + private val binder = LocalBinder() + + private var bluetoothAdapter: BluetoothAdapter? = null + private var bluetoothGatt: BluetoothGatt? = null + private var connectionState = STATE_DISCONNECTED + + // [START android_bluetooth_binder] + override fun onBind(intent: Intent): IBinder? { + return binder + } + + inner class LocalBinder : Binder() { + fun getService(): BluetoothLeService { + return this@BluetoothLeService + } + } + // [END android_bluetooth_binder] + + // [START android_bluetooth_initialize] + fun initialize(): Boolean { + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() + if (bluetoothAdapter == null) { + Log.e(TAG, "Unable to obtain a BluetoothAdapter.") + return false + } + return true + } + // [END android_bluetooth_initialize] + + // [START android_bluetooth_connect_simple] + fun simpleConnect(address: String): Boolean { + bluetoothAdapter?.let { adapter -> + try { + val device = adapter.getRemoteDevice(address) + } catch (exception: IllegalArgumentException) { + Log.w(TAG, "Device not found with provided address.") + return false + } + // connect to the GATT server on the device + return true + } ?: run { + Log.w(TAG, "BluetoothAdapter not initialized") + return false + } + } + // [END android_bluetooth_connect_simple] + + // [START android_bluetooth_connect] + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + fun connect(address: String): Boolean { + bluetoothAdapter?.let { adapter -> + try { + val device = adapter.getRemoteDevice(address) + // connect to the GATT server on the device + bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) + return true + } catch (exception: IllegalArgumentException) { + Log.w(TAG, "Device not found with provided address. Unable to connect.") + return false + } + } ?: run { + Log.w(TAG, "BluetoothAdapter not initialized") + return false + } + } + // [END android_bluetooth_connect] + + // [START android_bluetooth_callback_simple] + private val simpleBluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + } + } + } + // [END android_bluetooth_callback_simple] + + // [START android_bluetooth_callback] + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + connectionState = STATE_CONNECTED + broadcastUpdate(ACTION_GATT_CONNECTED) + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + connectionState = STATE_DISCONNECTED + broadcastUpdate(ACTION_GATT_DISCONNECTED) + } + } + } + // [END android_bluetooth_callback] + + // [START android_bluetooth_broadcast] + private fun broadcastUpdate(action: String) { + val intent = Intent(action) + sendBroadcast(intent) + } + // [END android_bluetooth_broadcast] + + // [START android_bluetooth_close] + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + override fun onUnbind(intent: Intent?): Boolean { + close() + return super.onUnbind(intent) + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + private fun close() { + bluetoothGatt?.let { gatt -> + gatt.close() + bluetoothGatt = null + } + } + // [END android_bluetooth_close] + + companion object { + // [START android_bluetooth_constants] + const val ACTION_GATT_CONNECTED = + "com.example.bluetooth.le.ACTION_GATT_CONNECTED" + const val ACTION_GATT_DISCONNECTED = + "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" + + private const val STATE_DISCONNECTED = 0 + private const val STATE_CONNECTED = 2 + // [END android_bluetooth_constants] + } +} + +// [END android_bluetooth_service_all] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt new file mode 100644 index 000000000..2678b7215 --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -0,0 +1,171 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.kotlin + +import android.Manifest +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.ServiceConnection +import android.os.Bundle +import android.os.IBinder +import android.util.Log +import androidx.annotation.RequiresPermission +import androidx.appcompat.app.AppCompatActivity +import com.sample.android.bluetoothle.R +import com.sample.android.bluetoothle.kotlin.BluetoothLeService.LocalBinder + +private const val TAG = "DeviceControlActivity" + +// [START android_bluetooth_activity_all] +class DeviceControlActivity : AppCompatActivity() { + + private var bluetoothService: BluetoothLeService? = null + private var deviceAddress: String? = null + private var connected = false + + // [START android_bluetooth_service_connection_simple] + // Code to manage Service lifecycle. + private val simpleServiceConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + // call functions on service to check connection and connect to devices + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + } + // [END android_bluetooth_service_connection_simple] + + // [START android_bluetooth_service_connection_initialize] + // Code to manage Service lifecycle. + private val initializeServiceConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + if (!bluetooth.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth") + finish() + } + // perform device connection + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + } + // [END android_bluetooth_service_connection_initialize] + + // [START android_bluetooth_service_connection] + + // Code to manage Service lifecycle. + private val serviceConnection: ServiceConnection = object : ServiceConnection { + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + // [START android_bluetooth_initialize_activity] + if (!bluetooth.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth") + finish() + } + // [END android_bluetooth_initialize_activity] + // perform device connection + // [START android_bluetooth_connect_activity] + bluetooth.connect(deviceAddress!!) + // [END android_bluetooth_connect_activity] + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + } + // [END android_bluetooth_service_connection] + + // [START android_bluetooth_update_receiver] + private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { + BluetoothLeService.ACTION_GATT_CONNECTED -> { + connected = true + updateConnectionState(R.string.connected) + } + BluetoothLeService.ACTION_GATT_DISCONNECTED -> { + connected = false + updateConnectionState(R.string.disconnected) + } + } + } + } + // [END android_bluetooth_update_receiver] + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.gatt_services_characteristics) + + deviceAddress = intent.getStringExtra("EXTRAS_DEVICE_ADDRESS") + + // [START android_bluetooth_bind_service] + val gattServiceIntent = Intent(this, BluetoothLeService::class.java) + bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) + // [END android_bluetooth_bind_service] + } + + // [START android_bluetooth_receiver_lifecycle] + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + override fun onResume() { + super.onResume() + registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) + if (bluetoothService != null) { + val result = bluetoothService!!.connect(deviceAddress!!) + Log.d(TAG, "Connect request result=$result") + } + } + + override fun onPause() { + super.onPause() + unregisterReceiver(gattUpdateReceiver) + } + private fun makeGattUpdateIntentFilter(): IntentFilter { + return IntentFilter().apply { + addAction(BluetoothLeService.ACTION_GATT_CONNECTED) + addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED) + } + } + // [END android_bluetooth_receiver_lifecycle] + + private fun updateConnectionState(resourceId: Int) { + // Dummy implementation + } +} +// [END android_bluetooth_activity_all] diff --git a/bluetoothle/src/main/res/layout/gatt_services_characteristics.xml b/bluetoothle/src/main/res/layout/gatt_services_characteristics.xml new file mode 100644 index 000000000..14140aada --- /dev/null +++ b/bluetoothle/src/main/res/layout/gatt_services_characteristics.xml @@ -0,0 +1,21 @@ + + + + diff --git a/bluetoothle/src/main/res/values/strings.xml b/bluetoothle/src/main/res/values/strings.xml index fa7796f20..1ebd1bd3b 100644 --- a/bluetoothle/src/main/res/values/strings.xml +++ b/bluetoothle/src/main/res/values/strings.xml @@ -14,4 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + Connected + Disconnected + From 0007881522da9d527ee1742c6fca11c1a75a065a Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Wed, 3 Jun 2026 10:35:40 +0100 Subject: [PATCH 02/21] fix(bluetoothle): add early return on initialization failure --- .../sample/android/bluetoothle/java/DeviceControlActivity.java | 1 + .../sample/android/bluetoothle/kotlin/DeviceControlActivity.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java index 92ddae9d1..ee99ab520 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -93,6 +93,7 @@ public void onServiceConnected(ComponentName name, IBinder service) { if (!bluetoothService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); + return; } // [END android_bluetooth_initialize_activity_java] // perform device connection diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index 2678b7215..0cb5fc0ea 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -97,6 +97,7 @@ class DeviceControlActivity : AppCompatActivity() { if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() + return@let } // [END android_bluetooth_initialize_activity] // perform device connection From 92111e294431b269d71235a63f8a8054003969f5 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 00:06:35 +0100 Subject: [PATCH 03/21] fix(bluetoothle): Address PR review comments on GATT callback and connection snippets - Implemented 'Silent Wrapper' technique to resolve name inconsistencies (simpleConnect -> connect, simpleBluetoothGattCallback -> bluetoothGattCallback, simpleServiceConnection -> serviceConnection) without breaking compilation. - Moved @RequiresPermission annotations outside of snippet region tags or wrapped them in silent excludes to match original documentation. - Renamed 'Dummy implementation' to 'Placeholder implementation'. - Removed temporary 'FIXED' comment in BluetoothLeService.java. - Cleaned up duplicate region tags in BluetoothLeService.kt. --- .../bluetoothle/java/BluetoothLeService.java | 62 +++++++++------- .../java/DeviceControlActivity.java | 72 +++++++++++-------- .../bluetoothle/kotlin/BluetoothLeService.kt | 52 +++++++++----- .../kotlin/DeviceControlActivity.kt | 70 ++++++++++-------- 4 files changed, 155 insertions(+), 101 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 40bad9204..284b95720 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -78,24 +78,30 @@ public boolean initialize() { // [END android_bluetooth_initialize_java] // [START android_bluetooth_connect_simple_java] - public boolean simpleConnect(final String address) { - if (bluetoothAdapter == null || address == null) { - Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); - return false; - } - try { - final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); - } catch (IllegalArgumentException exception) { - Log.w(TAG, "Device not found with provided address."); - return false; + // [START_EXCLUDE silent] + private class SimplifiedConnect { + public boolean connect(final String address) { + // [END_EXCLUDE] + if (bluetoothAdapter == null || address == null) { + Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); + return false; + } + try { + final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); + } catch (IllegalArgumentException exception) { + Log.w(TAG, "Device not found with provided address."); + return false; + } + // connect to the GATT server on the device + return true; + // [START_EXCLUDE silent] } - // connect to the GATT server on the device - return true; } + // [END_EXCLUDE] // [END android_bluetooth_connect_simple_java] - // [START android_bluetooth_connect_java] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [START android_bluetooth_connect_java] public boolean connect(final String address) { if (bluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); @@ -114,16 +120,22 @@ public boolean connect(final String address) { // [END android_bluetooth_connect_java] // [START android_bluetooth_callback_simple_java] - private final BluetoothGattCallback simpleBluetoothGattCallback = new BluetoothGattCallback() { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server + // [START_EXCLUDE silent] + private class SimplifiedCallback { + private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { + // [END_EXCLUDE] + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + } } - } - }; + // [START_EXCLUDE silent] + }; + } + // [END_EXCLUDE] // [END android_bluetooth_callback_simple_java] // [START android_bluetooth_callback_java] @@ -150,18 +162,20 @@ private void broadcastUpdate(final String action) { } // [END android_bluetooth_broadcast_java] - // [START android_bluetooth_close_java] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [START android_bluetooth_close_java] @Override public boolean onUnbind(Intent intent) { close(); return super.onUnbind(intent); } + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] private void close() { if (bluetoothGatt == null) { - return; // FIXED: lowercase 'return' + return; } bluetoothGatt.close(); bluetoothGatt = null; diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java index ee99ab520..1f94bc85f 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -44,48 +44,62 @@ public class DeviceControlActivity extends AppCompatActivity { private boolean connected = false; // [START android_bluetooth_service_connection_simple_java] - private final ServiceConnection simpleServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - bluetoothService = ((LocalBinder) service).getService(); - if (bluetoothService != null) { - // call functions on service to check connection and connect to devices + // [START_EXCLUDE silent] + private class SimplifiedServiceConnection { + private final ServiceConnection serviceConnection = new ServiceConnection() { + // [END_EXCLUDE] + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + bluetoothService = ((LocalBinder) service).getService(); + if (bluetoothService != null) { + // call functions on service to check connection and connect to devices + } } - } - @Override - public void onServiceDisconnected(ComponentName name) { - bluetoothService = null; - } - }; + @Override + public void onServiceDisconnected(ComponentName name) { + bluetoothService = null; + } + // [START_EXCLUDE silent] + }; + } + // [END_EXCLUDE] // [END android_bluetooth_service_connection_simple_java] // [START android_bluetooth_service_connection_initialize_java] - private final ServiceConnection initializeServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - bluetoothService = ((LocalBinder) service).getService(); - if (bluetoothService != null) { - if (!bluetoothService.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth"); - finish(); + // [START_EXCLUDE silent] + private class InitializeServiceConnection { + private final ServiceConnection serviceConnection = new ServiceConnection() { + // [END_EXCLUDE] + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + bluetoothService = ((LocalBinder) service).getService(); + if (bluetoothService != null) { + if (!bluetoothService.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth"); + finish(); + } + // perform device connection } - // perform device connection } - } - @Override - public void onServiceDisconnected(ComponentName name) { - bluetoothService = null; - } - }; + @Override + public void onServiceDisconnected(ComponentName name) { + bluetoothService = null; + } + // [START_EXCLUDE silent] + }; + } + // [END_EXCLUDE] // [END android_bluetooth_service_connection_initialize_java] // [START android_bluetooth_service_connection_java] private final ServiceConnection serviceConnection = new ServiceConnection() { @Override + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { @@ -139,9 +153,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // [END android_bluetooth_bind_service_java] } + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [START android_bluetooth_receiver_lifecycle_java] @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) protected void onResume() { super.onResume(); @@ -167,7 +181,7 @@ private static IntentFilter makeGattUpdateIntentFilter() { private void updateConnectionState(int resourceId) { - // Dummy implementation + // Placeholder implementation } } // [END android_bluetooth_activity_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index cc4b237bc..4383a6533 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -63,25 +63,31 @@ class BluetoothLeService : Service() { // [END android_bluetooth_initialize] // [START android_bluetooth_connect_simple] - fun simpleConnect(address: String): Boolean { - bluetoothAdapter?.let { adapter -> - try { - val device = adapter.getRemoteDevice(address) - } catch (exception: IllegalArgumentException) { - Log.w(TAG, "Device not found with provided address.") + // [START_EXCLUDE silent] + private inner class SimplifiedConnect { + fun connect(address: String): Boolean { + // [END_EXCLUDE] + bluetoothAdapter?.let { adapter -> + try { + val device = adapter.getRemoteDevice(address) + } catch (exception: IllegalArgumentException) { + Log.w(TAG, "Device not found with provided address.") + return false + } + // connect to the GATT server on the device + return true + } ?: run { + Log.w(TAG, "BluetoothAdapter not initialized") return false } - // connect to the GATT server on the device - return true - } ?: run { - Log.w(TAG, "BluetoothAdapter not initialized") - return false + // [START_EXCLUDE silent] } } + // [END_EXCLUDE] // [END android_bluetooth_connect_simple] - // [START android_bluetooth_connect] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [START android_bluetooth_connect] fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { @@ -101,15 +107,21 @@ class BluetoothLeService : Service() { // [END android_bluetooth_connect] // [START android_bluetooth_callback_simple] - private val simpleBluetoothGattCallback = object : BluetoothGattCallback() { - override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server + // [START_EXCLUDE silent] + private inner class SimplifiedCallback { + val bluetoothGattCallback = object : BluetoothGattCallback() { + // [END_EXCLUDE] + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + } } + // [START_EXCLUDE silent] } } + // [END_EXCLUDE] // [END android_bluetooth_callback_simple] // [START android_bluetooth_callback] @@ -135,14 +147,16 @@ class BluetoothLeService : Service() { } // [END android_bluetooth_broadcast] - // [START android_bluetooth_close] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [START android_bluetooth_close] override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) } + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] private fun close() { bluetoothGatt?.let { gatt -> gatt.close() diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index 0cb5fc0ea..f949c49aa 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -41,52 +41,64 @@ class DeviceControlActivity : AppCompatActivity() { private var connected = false // [START android_bluetooth_service_connection_simple] - // Code to manage Service lifecycle. - private val simpleServiceConnection: ServiceConnection = object : ServiceConnection { - override fun onServiceConnected( - componentName: ComponentName, - service: IBinder - ) { - bluetoothService = (service as LocalBinder).getService() - bluetoothService?.let { bluetooth -> - // call functions on service to check connection and connect to devices + // [START_EXCLUDE silent] + private inner class SimplifiedServiceConnection { + val serviceConnection: ServiceConnection = object : ServiceConnection { + // [END_EXCLUDE] + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + // call functions on service to check connection and connect to devices + } } - } - override fun onServiceDisconnected(componentName: ComponentName) { - bluetoothService = null + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + // [START_EXCLUDE silent] } } + // [END_EXCLUDE] // [END android_bluetooth_service_connection_simple] // [START android_bluetooth_service_connection_initialize] - // Code to manage Service lifecycle. - private val initializeServiceConnection: ServiceConnection = object : ServiceConnection { - override fun onServiceConnected( - componentName: ComponentName, - service: IBinder - ) { - bluetoothService = (service as LocalBinder).getService() - bluetoothService?.let { bluetooth -> - if (!bluetooth.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth") - finish() + // [START_EXCLUDE silent] + private inner class InitializeServiceConnection { + val serviceConnection: ServiceConnection = object : ServiceConnection { + // [END_EXCLUDE] + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + if (!bluetooth.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth") + finish() + } + // perform device connection } - // perform device connection } - } - override fun onServiceDisconnected(componentName: ComponentName) { - bluetoothService = null + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + // [START_EXCLUDE silent] } } + // [END_EXCLUDE] // [END android_bluetooth_service_connection_initialize] // [START android_bluetooth_service_connection] // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] override fun onServiceConnected( componentName: ComponentName, service: IBinder @@ -142,8 +154,8 @@ class DeviceControlActivity : AppCompatActivity() { // [END android_bluetooth_bind_service] } - // [START android_bluetooth_receiver_lifecycle] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [START android_bluetooth_receiver_lifecycle] override fun onResume() { super.onResume() registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) @@ -166,7 +178,7 @@ class DeviceControlActivity : AppCompatActivity() { // [END android_bluetooth_receiver_lifecycle] private fun updateConnectionState(resourceId: Int) { - // Dummy implementation + // Placeholder implementation } } // [END android_bluetooth_activity_all] From 4d88daa1be95cd456df4158a97742960edacb47c Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 00:23:31 +0100 Subject: [PATCH 04/21] fix(bluetoothle): merge update receiver and lifecycle snippets Extend update receiver regions in DeviceControlActivity (Java/Kotlin) to cover lifecycle methods, using silent excludes to hide onCreate and permission annotations. This matches the DAC documentation page structure exactly while keeping the repository code compilable. --- .../android/bluetoothle/java/DeviceControlActivity.java | 4 +++- .../android/bluetoothle/kotlin/DeviceControlActivity.kt | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java index 1f94bc85f..7035189ce 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -138,8 +138,8 @@ public void onReceive(Context context, Intent intent) { } } }; - // [END android_bluetooth_update_receiver_java] + // [START_EXCLUDE silent] @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -154,6 +154,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] // [START android_bluetooth_receiver_lifecycle_java] @Override protected void onResume() { @@ -178,6 +179,7 @@ private static IntentFilter makeGattUpdateIntentFilter() { return intentFilter; } // [END android_bluetooth_receiver_lifecycle_java] + // [END android_bluetooth_update_receiver_java] private void updateConnectionState(int resourceId) { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index f949c49aa..e6ad5ceed 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -140,8 +140,8 @@ class DeviceControlActivity : AppCompatActivity() { } } } - // [END android_bluetooth_update_receiver] + // [START_EXCLUDE silent] override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.gatt_services_characteristics) @@ -155,6 +155,7 @@ class DeviceControlActivity : AppCompatActivity() { } @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] // [START android_bluetooth_receiver_lifecycle] override fun onResume() { super.onResume() @@ -176,6 +177,7 @@ class DeviceControlActivity : AppCompatActivity() { } } // [END android_bluetooth_receiver_lifecycle] + // [END android_bluetooth_update_receiver] private fun updateConnectionState(resourceId: Int) { // Placeholder implementation From f57ca90cf3049fd1d66e3403806e43743da50864 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 00:32:55 +0100 Subject: [PATCH 05/21] fix(bluetoothle): migrate binder snippet Migrate the entire BluetoothLeService binder code block (including class declaration) into the repository as a new region tag 'android_bluetooth_service_binder'. Use silent exclusions to hide the rest of the class methods, ensuring the snippet is 100% generated from the source file without hardcoding wrappers in the markdown. --- .../android/bluetoothle/java/BluetoothLeService.java | 10 +++++++++- .../android/bluetoothle/kotlin/BluetoothLeService.kt | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 284b95720..6acad03d3 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -32,11 +32,15 @@ import androidx.annotation.Nullable; // [START android_bluetooth_service_all_java] +// [START android_bluetooth_service_binder_java] public class BluetoothLeService extends Service { - + // [START_EXCLUDE silent] public static final String TAG = "BluetoothLeService"; + // [END_EXCLUDE] private final Binder binder = new LocalBinder(); + + // [START_EXCLUDE silent] private BluetoothAdapter bluetoothAdapter; private BluetoothGatt bluetoothGatt; private int connectionState; @@ -50,6 +54,7 @@ public class BluetoothLeService extends Service { private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTED = 2; // [END android_bluetooth_constants_java] + // [END_EXCLUDE] // [START android_bluetooth_binder_java] @@ -65,6 +70,7 @@ public BluetoothLeService getService() { } } // [END android_bluetooth_binder_java] + // [START_EXCLUDE silent] // [START android_bluetooth_initialize_java] public boolean initialize() { @@ -181,6 +187,8 @@ private void close() { bluetoothGatt = null; } // [END android_bluetooth_close_java] + // [END_EXCLUDE] } +// [END android_bluetooth_service_binder_java] // [END android_bluetooth_service_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 4383a6533..17d597429 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -31,13 +31,16 @@ import androidx.annotation.RequiresPermission private const val TAG = "BluetoothLeService" // [START android_bluetooth_service_all] +// [START android_bluetooth_service_binder] class BluetoothLeService : Service() { private val binder = LocalBinder() + // [START_EXCLUDE silent] private var bluetoothAdapter: BluetoothAdapter? = null private var bluetoothGatt: BluetoothGatt? = null private var connectionState = STATE_DISCONNECTED + // [END_EXCLUDE] // [START android_bluetooth_binder] override fun onBind(intent: Intent): IBinder? { @@ -50,6 +53,7 @@ class BluetoothLeService : Service() { } } // [END android_bluetooth_binder] + // [START_EXCLUDE silent] // [START android_bluetooth_initialize] fun initialize(): Boolean { @@ -176,6 +180,8 @@ class BluetoothLeService : Service() { private const val STATE_CONNECTED = 2 // [END android_bluetooth_constants] } + // [END_EXCLUDE] } +// [END android_bluetooth_service_binder] // [END android_bluetooth_service_all] From 643d93f438ec37b175e74b623567271fc6e243dc Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 00:39:23 +0100 Subject: [PATCH 06/21] fix(bluetoothle): split binder snippet to new files Revert the risky nested exclusions in the main BluetoothLeService files. Instead, split the binder-only snippet into dedicated files in a separate 'binder' package (to avoid name clashes). This ensures the binder snippet is cleanly imported with its class wrapper, without using dangerous nested exclusions, and keeping all code 100% in the repository. --- .../bluetoothle/java/BluetoothLeService.java | 10 +---- .../java/binder/BluetoothLeService.java | 42 +++++++++++++++++++ .../bluetoothle/kotlin/BluetoothLeService.kt | 6 --- .../kotlin/binder/BluetoothLeService.kt | 39 +++++++++++++++++ 4 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 6acad03d3..284b95720 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -32,15 +32,11 @@ import androidx.annotation.Nullable; // [START android_bluetooth_service_all_java] -// [START android_bluetooth_service_binder_java] public class BluetoothLeService extends Service { - // [START_EXCLUDE silent] + public static final String TAG = "BluetoothLeService"; - // [END_EXCLUDE] private final Binder binder = new LocalBinder(); - - // [START_EXCLUDE silent] private BluetoothAdapter bluetoothAdapter; private BluetoothGatt bluetoothGatt; private int connectionState; @@ -54,7 +50,6 @@ public class BluetoothLeService extends Service { private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTED = 2; // [END android_bluetooth_constants_java] - // [END_EXCLUDE] // [START android_bluetooth_binder_java] @@ -70,7 +65,6 @@ public BluetoothLeService getService() { } } // [END android_bluetooth_binder_java] - // [START_EXCLUDE silent] // [START android_bluetooth_initialize_java] public boolean initialize() { @@ -187,8 +181,6 @@ private void close() { bluetoothGatt = null; } // [END android_bluetooth_close_java] - // [END_EXCLUDE] } -// [END android_bluetooth_service_binder_java] // [END android_bluetooth_service_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java new file mode 100644 index 000000000..c4386f061 --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java @@ -0,0 +1,42 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.java.binder; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import androidx.annotation.Nullable; + +// [START android_bluetooth_service_binder_java] +public class BluetoothLeService extends Service { + + private Binder binder = new LocalBinder(); + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + class LocalBinder extends Binder { + public BluetoothLeService getService() { + return BluetoothLeService.this; + } + } +} +// [END android_bluetooth_service_binder_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 17d597429..4383a6533 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -31,16 +31,13 @@ import androidx.annotation.RequiresPermission private const val TAG = "BluetoothLeService" // [START android_bluetooth_service_all] -// [START android_bluetooth_service_binder] class BluetoothLeService : Service() { private val binder = LocalBinder() - // [START_EXCLUDE silent] private var bluetoothAdapter: BluetoothAdapter? = null private var bluetoothGatt: BluetoothGatt? = null private var connectionState = STATE_DISCONNECTED - // [END_EXCLUDE] // [START android_bluetooth_binder] override fun onBind(intent: Intent): IBinder? { @@ -53,7 +50,6 @@ class BluetoothLeService : Service() { } } // [END android_bluetooth_binder] - // [START_EXCLUDE silent] // [START android_bluetooth_initialize] fun initialize(): Boolean { @@ -180,8 +176,6 @@ class BluetoothLeService : Service() { private const val STATE_CONNECTED = 2 // [END android_bluetooth_constants] } - // [END_EXCLUDE] } -// [END android_bluetooth_service_binder] // [END android_bluetooth_service_all] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt new file mode 100644 index 000000000..e22bc5af5 --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.kotlin.binder + +import android.app.Service +import android.content.Intent +import android.os.Binder +import android.os.IBinder + +// [START android_bluetooth_service_binder] +class BluetoothLeService : Service() { + + private val binder = LocalBinder() + + override fun onBind(intent: Intent): IBinder? { + return binder + } + + inner class LocalBinder : Binder() { + fun getService(): BluetoothLeService { + return this@BluetoothLeService + } + } +} +// [END android_bluetooth_service_binder] From c6470a5211d8fd87112f006dd2ce25627329fc6f Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 00:51:03 +0100 Subject: [PATCH 07/21] refactor(bluetoothle): remove unused binder tags Remove the old 'android_bluetooth_binder' and 'android_bluetooth_binder_java' region tags from the main BluetoothLeService files, as they have been replaced by the new dedicated binder snippets. --- .../com/sample/android/bluetoothle/java/BluetoothLeService.java | 2 -- .../com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt | 2 -- 2 files changed, 4 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 284b95720..7a0cc4988 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -52,7 +52,6 @@ public class BluetoothLeService extends Service { // [END android_bluetooth_constants_java] - // [START android_bluetooth_binder_java] @Nullable @Override public IBinder onBind(Intent intent) { @@ -64,7 +63,6 @@ public BluetoothLeService getService() { return BluetoothLeService.this; } } - // [END android_bluetooth_binder_java] // [START android_bluetooth_initialize_java] public boolean initialize() { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 4383a6533..9faf1f611 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -39,7 +39,6 @@ class BluetoothLeService : Service() { private var bluetoothGatt: BluetoothGatt? = null private var connectionState = STATE_DISCONNECTED - // [START android_bluetooth_binder] override fun onBind(intent: Intent): IBinder? { return binder } @@ -49,7 +48,6 @@ class BluetoothLeService : Service() { return this@BluetoothLeService } } - // [END android_bluetooth_binder] // [START android_bluetooth_initialize] fun initialize(): Boolean { From fb391502fd275e1e21a2bf7696e8b4b63d3800aa Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 00:56:11 +0100 Subject: [PATCH 08/21] refactor(bluetoothle): remove redundant tags Remove 'android_bluetooth_receiver_lifecycle' and 'android_bluetooth_receiver_lifecycle_java' tags from DeviceControlActivity files. These sub-regions are now redundant because they are fully merged into the parent 'android_bluetooth_update_receiver' regions to fix the snippet length issue. --- .../sample/android/bluetoothle/java/DeviceControlActivity.java | 2 -- .../sample/android/bluetoothle/kotlin/DeviceControlActivity.kt | 2 -- 2 files changed, 4 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java index 7035189ce..8e4d449c5 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -155,7 +155,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [END_EXCLUDE] - // [START android_bluetooth_receiver_lifecycle_java] @Override protected void onResume() { super.onResume(); @@ -178,7 +177,6 @@ private static IntentFilter makeGattUpdateIntentFilter() { intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); return intentFilter; } - // [END android_bluetooth_receiver_lifecycle_java] // [END android_bluetooth_update_receiver_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index e6ad5ceed..a6a4a2ea9 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -156,7 +156,6 @@ class DeviceControlActivity : AppCompatActivity() { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [END_EXCLUDE] - // [START android_bluetooth_receiver_lifecycle] override fun onResume() { super.onResume() registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) @@ -176,7 +175,6 @@ class DeviceControlActivity : AppCompatActivity() { addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED) } } - // [END android_bluetooth_receiver_lifecycle] // [END android_bluetooth_update_receiver] private fun updateConnectionState(resourceId: Int) { From bd730c212565ef2ea377e5a9396ed79d2bc82f30 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 00:59:29 +0100 Subject: [PATCH 09/21] feat(bluetoothle): add connectGatt, expand bind Add the 'android_bluetooth_connect_gatt' (and Java) tags around the connectGatt call in BluetoothLeService to allow importing it without class wrappers. Expand the 'android_bluetooth_bind_service' (and Java) tags to cover the entire onCreate method, enabling clean imports in the documentation. --- .../sample/android/bluetoothle/java/BluetoothLeService.java | 2 ++ .../android/bluetoothle/java/DeviceControlActivity.java | 4 ++-- .../sample/android/bluetoothle/kotlin/BluetoothLeService.kt | 2 ++ .../android/bluetoothle/kotlin/DeviceControlActivity.kt | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 7a0cc4988..4b875a469 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -108,7 +108,9 @@ public boolean connect(final String address) { try { final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); // connect to the GATT server on the device + // [START android_bluetooth_connect_gatt_java] bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback); + // [END android_bluetooth_connect_gatt_java] return true; } catch (IllegalArgumentException exception) { Log.w(TAG, "Device not found with provided address. Unable to connect."); diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java index 8e4d449c5..64850ac0a 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -140,6 +140,7 @@ public void onReceive(Context context, Intent intent) { }; // [START_EXCLUDE silent] + // [START android_bluetooth_bind_service_java] @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -147,11 +148,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { deviceAddress = getIntent().getStringExtra("EXTRAS_DEVICE_ADDRESS"); - // [START android_bluetooth_bind_service_java] Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); - // [END android_bluetooth_bind_service_java] } + // [END android_bluetooth_bind_service_java] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [END_EXCLUDE] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 9faf1f611..411b34d3f 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -91,7 +91,9 @@ class BluetoothLeService : Service() { try { val device = adapter.getRemoteDevice(address) // connect to the GATT server on the device + // [START android_bluetooth_connect_gatt] bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) + // [END android_bluetooth_connect_gatt] return true } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address. Unable to connect.") diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index a6a4a2ea9..cb279e1f5 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -142,17 +142,17 @@ class DeviceControlActivity : AppCompatActivity() { } // [START_EXCLUDE silent] + // [START android_bluetooth_bind_service] override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.gatt_services_characteristics) deviceAddress = intent.getStringExtra("EXTRAS_DEVICE_ADDRESS") - // [START android_bluetooth_bind_service] val gattServiceIntent = Intent(this, BluetoothLeService::class.java) bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) - // [END android_bluetooth_bind_service] } + // [END android_bluetooth_bind_service] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [END_EXCLUDE] From aece89bdf352aa94eab9dbe724ae8bb5f82bb756 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 01:03:47 +0100 Subject: [PATCH 10/21] refactor(bluetoothle): revert split binder files Now that we have simplified the documentation to remove all hardcoded class wrappers, the binder snippet also no longer needs the class wrapper. Thus, we can revert the split and move the binder snippet tags back to the main BluetoothLeService files, deleting the redundant 'binder' package files. --- .../bluetoothle/java/BluetoothLeService.java | 2 + .../java/binder/BluetoothLeService.java | 42 ------------------- .../bluetoothle/kotlin/BluetoothLeService.kt | 2 + .../kotlin/binder/BluetoothLeService.kt | 39 ----------------- 4 files changed, 4 insertions(+), 81 deletions(-) delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 4b875a469..1ec7be15a 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -52,6 +52,7 @@ public class BluetoothLeService extends Service { // [END android_bluetooth_constants_java] + // [START android_bluetooth_binder_java] @Nullable @Override public IBinder onBind(Intent intent) { @@ -63,6 +64,7 @@ public BluetoothLeService getService() { return BluetoothLeService.this; } } + // [END android_bluetooth_binder_java] // [START android_bluetooth_initialize_java] public boolean initialize() { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java deleted file mode 100644 index c4386f061..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/binder/BluetoothLeService.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.java.binder; - -import android.app.Service; -import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; -import androidx.annotation.Nullable; - -// [START android_bluetooth_service_binder_java] -public class BluetoothLeService extends Service { - - private Binder binder = new LocalBinder(); - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return binder; - } - - class LocalBinder extends Binder { - public BluetoothLeService getService() { - return BluetoothLeService.this; - } - } -} -// [END android_bluetooth_service_binder_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 411b34d3f..bc0600712 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -39,6 +39,7 @@ class BluetoothLeService : Service() { private var bluetoothGatt: BluetoothGatt? = null private var connectionState = STATE_DISCONNECTED + // [START android_bluetooth_binder] override fun onBind(intent: Intent): IBinder? { return binder } @@ -48,6 +49,7 @@ class BluetoothLeService : Service() { return this@BluetoothLeService } } + // [END android_bluetooth_binder] // [START android_bluetooth_initialize] fun initialize(): Boolean { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt deleted file mode 100644 index e22bc5af5..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/binder/BluetoothLeService.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.kotlin.binder - -import android.app.Service -import android.content.Intent -import android.os.Binder -import android.os.IBinder - -// [START android_bluetooth_service_binder] -class BluetoothLeService : Service() { - - private val binder = LocalBinder() - - override fun onBind(intent: Intent): IBinder? { - return binder - } - - inner class LocalBinder : Binder() { - fun getService(): BluetoothLeService { - return this@BluetoothLeService - } - } -} -// [END android_bluetooth_service_binder] From 8a6b2bad4fdca58eab69b56c67e3ea2ecdf9b70d Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 01:32:29 +0100 Subject: [PATCH 11/21] refactor(ble): merge callback and bind tags Merge consecutive region tags into single combined tags to simplify the documentation imports and remove redundant splits: 1. Merge 'service_connection' and 'bind_service' in DeviceControlActivity by reordering onCreate to be immediately after serviceConnection. 2. Merge 'callback' and 'constants' in BluetoothLeService by reordering the companion object and callback to the top of the class. Also fix the silent wrapper tagging in BluetoothLeService callback to ensure the declaration and its closing brace are rendered. --- .../bluetoothle/java/BluetoothLeService.java | 39 ++++++------ .../java/DeviceControlActivity.java | 29 ++++----- .../bluetoothle/kotlin/BluetoothLeService.kt | 63 ++++++++++--------- .../kotlin/DeviceControlActivity.kt | 27 ++++---- 4 files changed, 77 insertions(+), 81 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 1ec7be15a..364885e17 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -41,7 +41,7 @@ public class BluetoothLeService extends Service { private BluetoothGatt bluetoothGatt; private int connectionState; - // [START android_bluetooth_constants_java] + // [START android_bluetooth_callback_java] public static final String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public static final String ACTION_GATT_DISCONNECTED = @@ -49,7 +49,22 @@ public class BluetoothLeService extends Service { private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTED = 2; - // [END android_bluetooth_constants_java] + + private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + connectionState = STATE_CONNECTED; + broadcastUpdate(ACTION_GATT_CONNECTED); + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + connectionState = STATE_DISCONNECTED; + broadcastUpdate(ACTION_GATT_DISCONNECTED); + } + } + }; + // [END android_bluetooth_callback_java] // [START android_bluetooth_binder_java] @@ -124,8 +139,8 @@ public boolean connect(final String address) { // [START android_bluetooth_callback_simple_java] // [START_EXCLUDE silent] private class SimplifiedCallback { - private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { // [END_EXCLUDE] + private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { @@ -134,28 +149,12 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState // disconnected from the GATT Server } } - // [START_EXCLUDE silent] }; + // [START_EXCLUDE silent] } // [END_EXCLUDE] // [END android_bluetooth_callback_simple_java] - // [START android_bluetooth_callback_java] - private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - connectionState = STATE_CONNECTED; - broadcastUpdate(ACTION_GATT_CONNECTED); - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server - connectionState = STATE_DISCONNECTED; - broadcastUpdate(ACTION_GATT_DISCONNECTED); - } - } - }; - // [END android_bluetooth_callback_java] // [START android_bluetooth_broadcast_java] private void broadcastUpdate(final String action) { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java index 64850ac0a..3521a558c 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -93,8 +93,7 @@ public void onServiceDisconnected(ComponentName name) { // [END_EXCLUDE] // [END android_bluetooth_service_connection_initialize_java] - // [START android_bluetooth_service_connection_java] - + // [START android_bluetooth_bind_service_java] private final ServiceConnection serviceConnection = new ServiceConnection() { @Override // [START_EXCLUDE silent] @@ -122,7 +121,18 @@ public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } }; - // [END android_bluetooth_service_connection_java] + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.gatt_services_characteristics); + + deviceAddress = getIntent().getStringExtra("EXTRAS_DEVICE_ADDRESS"); + + Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); + bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); + } + // [END android_bluetooth_bind_service_java] // [START android_bluetooth_update_receiver_java] private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() { @@ -140,19 +150,6 @@ public void onReceive(Context context, Intent intent) { }; // [START_EXCLUDE silent] - // [START android_bluetooth_bind_service_java] - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.gatt_services_characteristics); - - deviceAddress = getIntent().getStringExtra("EXTRAS_DEVICE_ADDRESS"); - - Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); - bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); - } - // [END android_bluetooth_bind_service_java] - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [END_EXCLUDE] @Override diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index bc0600712..7bf4c4020 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -39,6 +39,37 @@ class BluetoothLeService : Service() { private var bluetoothGatt: BluetoothGatt? = null private var connectionState = STATE_DISCONNECTED + // [START android_bluetooth_callback] + companion object { + const val ACTION_GATT_CONNECTED = + "com.example.bluetooth.le.ACTION_GATT_CONNECTED" + const val ACTION_GATT_DISCONNECTED = + "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" + + private const val STATE_DISCONNECTED = 0 + private const val STATE_CONNECTED = 2 + } + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + val intentAction: String + if (newState == BluetoothProfile.STATE_CONNECTED) { + intentAction = ACTION_GATT_CONNECTED + connectionState = STATE_CONNECTED + broadcastUpdate(intentAction) + Log.i(TAG, "Connected to GATT server.") + Log.i(TAG, "Attempting to start service discovery: " + bluetoothGatt?.discoverServices()) + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + intentAction = ACTION_GATT_DISCONNECTED + connectionState = STATE_DISCONNECTED + Log.i(TAG, "Disconnected from GATT server.") + broadcastUpdate(intentAction) + } + } + } + // [END android_bluetooth_callback] + // [START android_bluetooth_binder] override fun onBind(intent: Intent): IBinder? { return binder @@ -111,8 +142,8 @@ class BluetoothLeService : Service() { // [START android_bluetooth_callback_simple] // [START_EXCLUDE silent] private inner class SimplifiedCallback { + // [END_EXCLUDE] val bluetoothGattCallback = object : BluetoothGattCallback() { - // [END_EXCLUDE] override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server @@ -120,28 +151,12 @@ class BluetoothLeService : Service() { // disconnected from the GATT Server } } - // [START_EXCLUDE silent] } + // [START_EXCLUDE silent] } // [END_EXCLUDE] // [END android_bluetooth_callback_simple] - // [START android_bluetooth_callback] - private val bluetoothGattCallback = object : BluetoothGattCallback() { - override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - connectionState = STATE_CONNECTED - broadcastUpdate(ACTION_GATT_CONNECTED) - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server - connectionState = STATE_DISCONNECTED - broadcastUpdate(ACTION_GATT_DISCONNECTED) - } - } - } - // [END android_bluetooth_callback] - // [START android_bluetooth_broadcast] private fun broadcastUpdate(action: String) { val intent = Intent(action) @@ -166,18 +181,6 @@ class BluetoothLeService : Service() { } } // [END android_bluetooth_close] - - companion object { - // [START android_bluetooth_constants] - const val ACTION_GATT_CONNECTED = - "com.example.bluetooth.le.ACTION_GATT_CONNECTED" - const val ACTION_GATT_DISCONNECTED = - "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" - - private const val STATE_DISCONNECTED = 0 - private const val STATE_CONNECTED = 2 - // [END android_bluetooth_constants] - } } // [END android_bluetooth_service_all] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index cb279e1f5..2e715a953 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -92,8 +92,7 @@ class DeviceControlActivity : AppCompatActivity() { // [END_EXCLUDE] // [END android_bluetooth_service_connection_initialize] - // [START android_bluetooth_service_connection] - + // [START android_bluetooth_bind_service] // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { // [START_EXCLUDE silent] @@ -123,7 +122,17 @@ class DeviceControlActivity : AppCompatActivity() { bluetoothService = null } } - // [END android_bluetooth_service_connection] + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.gatt_services_characteristics) + + deviceAddress = intent.getStringExtra("EXTRAS_DEVICE_ADDRESS") + + val gattServiceIntent = Intent(this, BluetoothLeService::class.java) + bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) + } + // [END android_bluetooth_bind_service] // [START android_bluetooth_update_receiver] private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { @@ -142,18 +151,6 @@ class DeviceControlActivity : AppCompatActivity() { } // [START_EXCLUDE silent] - // [START android_bluetooth_bind_service] - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.gatt_services_characteristics) - - deviceAddress = intent.getStringExtra("EXTRAS_DEVICE_ADDRESS") - - val gattServiceIntent = Intent(this, BluetoothLeService::class.java) - bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) - } - // [END android_bluetooth_bind_service] - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [END_EXCLUDE] override fun onResume() { From 8e690bab74c25ffa62ee6810b52b42953052857e Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Sat, 6 Jun 2026 01:36:27 +0100 Subject: [PATCH 12/21] style(ble): spotless formatting fix Apply spotless formatting fix to BluetoothLeService.kt. --- .../com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 7bf4c4020..0e3e6f9dd 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -142,7 +142,7 @@ class BluetoothLeService : Service() { // [START android_bluetooth_callback_simple] // [START_EXCLUDE silent] private inner class SimplifiedCallback { - // [END_EXCLUDE] + // [END_EXCLUDE] val bluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { From 16671043c4d1e7d60e116f26cf8fb188b4e8353a Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Mon, 8 Jun 2026 14:27:24 +0100 Subject: [PATCH 13/21] refactor(bluetooth): Restore class wrappers and split snippets to avoid nesting --- .../bluetoothle/java/BluetoothLeService.java | 53 ++---------- .../java/BluetoothLeServiceSimple.java | 59 +++++++++++++ .../java/DeviceControlActivity.java | 60 +------------- .../java/DeviceControlActivitySimple.java | 79 ++++++++++++++++++ .../bluetoothle/kotlin/BluetoothLeService.kt | 52 ++---------- .../kotlin/BluetoothLeServiceSimple.kt | 56 +++++++++++++ .../kotlin/DeviceControlActivity.kt | 60 +------------- .../kotlin/DeviceControlActivitySimple.kt | 83 +++++++++++++++++++ 8 files changed, 297 insertions(+), 205 deletions(-) create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt create mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java index 364885e17..f266231ae 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java @@ -32,11 +32,13 @@ import androidx.annotation.Nullable; // [START android_bluetooth_service_all_java] +// [START android_bluetooth_binder_java] public class BluetoothLeService extends Service { public static final String TAG = "BluetoothLeService"; private final Binder binder = new LocalBinder(); + // [START_EXCLUDE silent] private BluetoothAdapter bluetoothAdapter; private BluetoothGatt bluetoothGatt; private int connectionState; @@ -65,9 +67,9 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState } }; // [END android_bluetooth_callback_java] + // [END_EXCLUDE] - // [START android_bluetooth_binder_java] @Nullable @Override public IBinder onBind(Intent intent) { @@ -79,7 +81,7 @@ public BluetoothLeService getService() { return BluetoothLeService.this; } } - // [END android_bluetooth_binder_java] + // [START_EXCLUDE silent] // [START android_bluetooth_initialize_java] public boolean initialize() { @@ -92,29 +94,6 @@ public boolean initialize() { } // [END android_bluetooth_initialize_java] - // [START android_bluetooth_connect_simple_java] - // [START_EXCLUDE silent] - private class SimplifiedConnect { - public boolean connect(final String address) { - // [END_EXCLUDE] - if (bluetoothAdapter == null || address == null) { - Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); - return false; - } - try { - final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); - } catch (IllegalArgumentException exception) { - Log.w(TAG, "Device not found with provided address."); - return false; - } - // connect to the GATT server on the device - return true; - // [START_EXCLUDE silent] - } - } - // [END_EXCLUDE] - // [END android_bluetooth_connect_simple_java] - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [START android_bluetooth_connect_java] public boolean connect(final String address) { @@ -136,26 +115,6 @@ public boolean connect(final String address) { } // [END android_bluetooth_connect_java] - // [START android_bluetooth_callback_simple_java] - // [START_EXCLUDE silent] - private class SimplifiedCallback { - // [END_EXCLUDE] - private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server - } - } - }; - // [START_EXCLUDE silent] - } - // [END_EXCLUDE] - // [END android_bluetooth_callback_simple_java] - - // [START android_bluetooth_broadcast_java] private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); @@ -171,9 +130,7 @@ public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } - // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] private void close() { if (bluetoothGatt == null) { return; @@ -182,6 +139,8 @@ private void close() { bluetoothGatt = null; } // [END android_bluetooth_close_java] + // [END_EXCLUDE] } +// [END android_bluetooth_binder_java] // [END android_bluetooth_service_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java new file mode 100644 index 000000000..92f891c12 --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java @@ -0,0 +1,59 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.java; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothProfile; +import android.util.Log; + +public class BluetoothLeServiceSimple { + private static final String TAG = "BluetoothLeService"; + private static BluetoothAdapter bluetoothAdapter; + + // [START android_bluetooth_connect_simple_java] + public boolean connect(final String address) { + if (bluetoothAdapter == null || address == null) { + Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); + return false; + } + try { + final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); + } catch (IllegalArgumentException exception) { + Log.w(TAG, "Device not found with provided address."); + return false; + } + // connect to the GATT server on the device + return true; + } + // [END android_bluetooth_connect_simple_java] + + // [START android_bluetooth_callback_simple_java] + private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + } + } + }; + // [END android_bluetooth_callback_simple_java] +} diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java index 3521a558c..c92e3f62d 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java @@ -32,9 +32,8 @@ import com.sample.android.bluetoothle.java.BluetoothLeService.LocalBinder; import androidx.annotation.RequiresPermission; - - // [START android_bluetooth_activity_all_java] +// [START android_bluetooth_bind_service_java] public class DeviceControlActivity extends AppCompatActivity { private static final String TAG = "DeviceControlActivity"; @@ -42,63 +41,12 @@ public class DeviceControlActivity extends AppCompatActivity { private BluetoothLeService bluetoothService; private String deviceAddress; private boolean connected = false; - - // [START android_bluetooth_service_connection_simple_java] - // [START_EXCLUDE silent] - private class SimplifiedServiceConnection { - private final ServiceConnection serviceConnection = new ServiceConnection() { - // [END_EXCLUDE] - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - bluetoothService = ((LocalBinder) service).getService(); - if (bluetoothService != null) { - // call functions on service to check connection and connect to devices - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - bluetoothService = null; - } - // [START_EXCLUDE silent] - }; - } - // [END_EXCLUDE] - // [END android_bluetooth_service_connection_simple_java] - - // [START android_bluetooth_service_connection_initialize_java] // [START_EXCLUDE silent] - private class InitializeServiceConnection { - private final ServiceConnection serviceConnection = new ServiceConnection() { - // [END_EXCLUDE] - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - bluetoothService = ((LocalBinder) service).getService(); - if (bluetoothService != null) { - if (!bluetoothService.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth"); - finish(); - } - // perform device connection - } - } - @Override - public void onServiceDisconnected(ComponentName name) { - bluetoothService = null; - } - // [START_EXCLUDE silent] - }; - } // [END_EXCLUDE] - // [END android_bluetooth_service_connection_initialize_java] - - // [START android_bluetooth_bind_service_java] private final ServiceConnection serviceConnection = new ServiceConnection() { @Override - // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { @@ -132,7 +80,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } - // [END android_bluetooth_bind_service_java] + // [START_EXCLUDE silent] // [START android_bluetooth_update_receiver_java] private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() { @@ -149,9 +97,7 @@ public void onReceive(Context context, Intent intent) { } }; - // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] @Override protected void onResume() { super.onResume(); @@ -180,5 +126,7 @@ private static IntentFilter makeGattUpdateIntentFilter() { private void updateConnectionState(int resourceId) { // Placeholder implementation } + // [END_EXCLUDE] } +// [END android_bluetooth_bind_service_java] // [END android_bluetooth_activity_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java new file mode 100644 index 000000000..b619781de --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java @@ -0,0 +1,79 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.java; + +import android.content.ComponentName; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.util.Log; +import com.sample.android.bluetoothle.java.BluetoothLeService.LocalBinder; + +public class DeviceControlActivitySimple { + private static final String TAG = "DeviceControlActivity"; + private static BluetoothLeService bluetoothService; + private static void finish() {} + + // [START android_bluetooth_service_connection_simple_java] + // [START_EXCLUDE silent] + private class SimplifiedServiceConnection { + private final ServiceConnection serviceConnection = new ServiceConnection() { + // [END_EXCLUDE] + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + bluetoothService = ((LocalBinder) service).getService(); + if (bluetoothService != null) { + // call functions on service to check connection and connect to devices + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + bluetoothService = null; + } + // [START_EXCLUDE silent] + }; + } + // [END_EXCLUDE] + // [END android_bluetooth_service_connection_simple_java] + + // [START android_bluetooth_service_connection_initialize_java] + // [START_EXCLUDE silent] + private class InitializeServiceConnection { + private final ServiceConnection serviceConnection = new ServiceConnection() { + // [END_EXCLUDE] + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + bluetoothService = ((LocalBinder) service).getService(); + if (bluetoothService != null) { + if (!bluetoothService.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth"); + finish(); + } + // perform device connection + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + bluetoothService = null; + } + // [START_EXCLUDE silent] + }; + } + // [END_EXCLUDE] + // [END android_bluetooth_service_connection_initialize_java] +} diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 0e3e6f9dd..1260d6d43 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -31,9 +31,11 @@ import androidx.annotation.RequiresPermission private const val TAG = "BluetoothLeService" // [START android_bluetooth_service_all] +// [START android_bluetooth_binder] class BluetoothLeService : Service() { private val binder = LocalBinder() + // [START_EXCLUDE silent] private var bluetoothAdapter: BluetoothAdapter? = null private var bluetoothGatt: BluetoothGatt? = null @@ -69,8 +71,8 @@ class BluetoothLeService : Service() { } } // [END android_bluetooth_callback] + // [END_EXCLUDE] - // [START android_bluetooth_binder] override fun onBind(intent: Intent): IBinder? { return binder } @@ -80,7 +82,7 @@ class BluetoothLeService : Service() { return this@BluetoothLeService } } - // [END android_bluetooth_binder] + // [START_EXCLUDE silent] // [START android_bluetooth_initialize] fun initialize(): Boolean { @@ -93,30 +95,6 @@ class BluetoothLeService : Service() { } // [END android_bluetooth_initialize] - // [START android_bluetooth_connect_simple] - // [START_EXCLUDE silent] - private inner class SimplifiedConnect { - fun connect(address: String): Boolean { - // [END_EXCLUDE] - bluetoothAdapter?.let { adapter -> - try { - val device = adapter.getRemoteDevice(address) - } catch (exception: IllegalArgumentException) { - Log.w(TAG, "Device not found with provided address.") - return false - } - // connect to the GATT server on the device - return true - } ?: run { - Log.w(TAG, "BluetoothAdapter not initialized") - return false - } - // [START_EXCLUDE silent] - } - } - // [END_EXCLUDE] - // [END android_bluetooth_connect_simple] - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [START android_bluetooth_connect] fun connect(address: String): Boolean { @@ -139,24 +117,6 @@ class BluetoothLeService : Service() { } // [END android_bluetooth_connect] - // [START android_bluetooth_callback_simple] - // [START_EXCLUDE silent] - private inner class SimplifiedCallback { - // [END_EXCLUDE] - val bluetoothGattCallback = object : BluetoothGattCallback() { - override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server - } - } - } - // [START_EXCLUDE silent] - } - // [END_EXCLUDE] - // [END android_bluetooth_callback_simple] - // [START android_bluetooth_broadcast] private fun broadcastUpdate(action: String) { val intent = Intent(action) @@ -171,9 +131,7 @@ class BluetoothLeService : Service() { return super.onUnbind(intent) } - // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] private fun close() { bluetoothGatt?.let { gatt -> gatt.close() @@ -181,6 +139,8 @@ class BluetoothLeService : Service() { } } // [END android_bluetooth_close] + // [END_EXCLUDE] } +// [END android_bluetooth_binder] // [END android_bluetooth_service_all] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt new file mode 100644 index 000000000..a0e231070 --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.kotlin + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCallback +import android.bluetooth.BluetoothProfile +import android.util.Log + +private var bluetoothAdapter: BluetoothAdapter? = null +private const val TAG = "BluetoothLeService" + +// [START android_bluetooth_connect_simple] +fun connect(address: String): Boolean { + bluetoothAdapter?.let { adapter -> + try { + val device = adapter.getRemoteDevice(address) + } catch (exception: IllegalArgumentException) { + Log.w(TAG, "Device not found with provided address.") + return false + } + // connect to the GATT server on the device + return true + } ?: run { + Log.w(TAG, "BluetoothAdapter not initialized") + return false + } +} +// [END android_bluetooth_connect_simple] + +// [START android_bluetooth_callback_simple] +val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + } + } +} +// [END android_bluetooth_callback_simple] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index 2e715a953..07de113cd 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -34,70 +34,18 @@ import com.sample.android.bluetoothle.kotlin.BluetoothLeService.LocalBinder private const val TAG = "DeviceControlActivity" // [START android_bluetooth_activity_all] +// [START android_bluetooth_bind_service] class DeviceControlActivity : AppCompatActivity() { private var bluetoothService: BluetoothLeService? = null private var deviceAddress: String? = null private var connected = false - - // [START android_bluetooth_service_connection_simple] - // [START_EXCLUDE silent] - private inner class SimplifiedServiceConnection { - val serviceConnection: ServiceConnection = object : ServiceConnection { - // [END_EXCLUDE] - override fun onServiceConnected( - componentName: ComponentName, - service: IBinder - ) { - bluetoothService = (service as LocalBinder).getService() - bluetoothService?.let { bluetooth -> - // call functions on service to check connection and connect to devices - } - } - - override fun onServiceDisconnected(componentName: ComponentName) { - bluetoothService = null - } - // [START_EXCLUDE silent] - } - } - // [END_EXCLUDE] - // [END android_bluetooth_service_connection_simple] - - // [START android_bluetooth_service_connection_initialize] // [START_EXCLUDE silent] - private inner class InitializeServiceConnection { - val serviceConnection: ServiceConnection = object : ServiceConnection { - // [END_EXCLUDE] - override fun onServiceConnected( - componentName: ComponentName, - service: IBinder - ) { - bluetoothService = (service as LocalBinder).getService() - bluetoothService?.let { bluetooth -> - if (!bluetooth.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth") - finish() - } - // perform device connection - } - } - override fun onServiceDisconnected(componentName: ComponentName) { - bluetoothService = null - } - // [START_EXCLUDE silent] - } - } // [END_EXCLUDE] - // [END android_bluetooth_service_connection_initialize] - - // [START android_bluetooth_bind_service] // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { - // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] override fun onServiceConnected( componentName: ComponentName, service: IBinder @@ -132,7 +80,7 @@ class DeviceControlActivity : AppCompatActivity() { val gattServiceIntent = Intent(this, BluetoothLeService::class.java) bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) } - // [END android_bluetooth_bind_service] + // [START_EXCLUDE silent] // [START android_bluetooth_update_receiver] private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { @@ -150,9 +98,7 @@ class DeviceControlActivity : AppCompatActivity() { } } - // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] override fun onResume() { super.onResume() registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) @@ -177,5 +123,7 @@ class DeviceControlActivity : AppCompatActivity() { private fun updateConnectionState(resourceId: Int) { // Placeholder implementation } + // [END_EXCLUDE] } +// [END android_bluetooth_bind_service] // [END android_bluetooth_activity_all] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt new file mode 100644 index 000000000..c5a847fff --- /dev/null +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2026 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 + * + * https://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 com.sample.android.bluetoothle.kotlin + +import android.content.ComponentName +import android.content.ServiceConnection +import android.os.IBinder +import android.util.Log +import com.sample.android.bluetoothle.kotlin.BluetoothLeService.LocalBinder + +// Placeholders for compilation +private var bluetoothService: BluetoothLeService? = null +private const val TAG = "DeviceControlActivity" +private fun finish() {} + +class DeviceControlActivitySimple { + + // [START android_bluetooth_service_connection_simple] + // [START_EXCLUDE silent] + private inner class SimplifiedServiceConnection { + val serviceConnection: ServiceConnection = object : ServiceConnection { + // [END_EXCLUDE] + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + // call functions on service to check connection and connect to devices + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + // [START_EXCLUDE silent] + } + } + // [END_EXCLUDE] + // [END android_bluetooth_service_connection_simple] + + // [START android_bluetooth_service_connection_initialize] + // [START_EXCLUDE silent] + private inner class InitializeServiceConnection { + val serviceConnection: ServiceConnection = object : ServiceConnection { + // [END_EXCLUDE] + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + if (!bluetooth.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth") + finish() + } + // perform device connection + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + // [START_EXCLUDE silent] + } + } + // [END_EXCLUDE] + // [END android_bluetooth_service_connection_initialize] +} From 5a135dde28f76569e8a72d088855155853a9edb5 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Wed, 10 Jun 2026 16:15:42 +0100 Subject: [PATCH 14/21] refactor(bluetooth): align Kotlin snippets Remove unused Java snippet files and update Kotlin snippets to use silent wrappers and correct region tags to match DevSite structure. --- .../bluetoothle/java/BluetoothLeService.java | 146 ------------------ .../java/BluetoothLeServiceSimple.java | 59 ------- .../java/DeviceControlActivity.java | 132 ---------------- .../java/DeviceControlActivitySimple.java | 79 ---------- .../bluetoothle/kotlin/BluetoothLeService.kt | 4 + .../kotlin/BluetoothLeServiceSimple.kt | 2 + .../kotlin/DeviceControlActivity.kt | 11 +- .../kotlin/DeviceControlActivitySimple.kt | 39 ++++- 8 files changed, 52 insertions(+), 420 deletions(-) delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java deleted file mode 100644 index f266231ae..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeService.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.java; - -import android.Manifest; -import android.app.Service; -import androidx.annotation.RequiresPermission; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallback; -import android.bluetooth.BluetoothProfile; -import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; -import android.util.Log; -import androidx.annotation.Nullable; - -// [START android_bluetooth_service_all_java] -// [START android_bluetooth_binder_java] -public class BluetoothLeService extends Service { - - public static final String TAG = "BluetoothLeService"; - - private final Binder binder = new LocalBinder(); - // [START_EXCLUDE silent] - private BluetoothAdapter bluetoothAdapter; - private BluetoothGatt bluetoothGatt; - private int connectionState; - - // [START android_bluetooth_callback_java] - public static final String ACTION_GATT_CONNECTED = - "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; - public static final String ACTION_GATT_DISCONNECTED = - "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; - - private static final int STATE_DISCONNECTED = 0; - private static final int STATE_CONNECTED = 2; - - private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - connectionState = STATE_CONNECTED; - broadcastUpdate(ACTION_GATT_CONNECTED); - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server - connectionState = STATE_DISCONNECTED; - broadcastUpdate(ACTION_GATT_DISCONNECTED); - } - } - }; - // [END android_bluetooth_callback_java] - // [END_EXCLUDE] - - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return binder; - } - - public class LocalBinder extends Binder { - public BluetoothLeService getService() { - return BluetoothLeService.this; - } - } - // [START_EXCLUDE silent] - - // [START android_bluetooth_initialize_java] - public boolean initialize() { - bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - if (bluetoothAdapter == null) { - Log.e(TAG, "Unable to obtain a BluetoothAdapter."); - return false; - } - return true; - } - // [END android_bluetooth_initialize_java] - - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [START android_bluetooth_connect_java] - public boolean connect(final String address) { - if (bluetoothAdapter == null || address == null) { - Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); - return false; - } - try { - final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); - // connect to the GATT server on the device - // [START android_bluetooth_connect_gatt_java] - bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback); - // [END android_bluetooth_connect_gatt_java] - return true; - } catch (IllegalArgumentException exception) { - Log.w(TAG, "Device not found with provided address. Unable to connect."); - return false; - } - } - // [END android_bluetooth_connect_java] - - // [START android_bluetooth_broadcast_java] - private void broadcastUpdate(final String action) { - final Intent intent = new Intent(action); - sendBroadcast(intent); - } - // [END android_bluetooth_broadcast_java] - - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [START android_bluetooth_close_java] - @Override - public boolean onUnbind(Intent intent) { - close(); - return super.onUnbind(intent); - } - - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - private void close() { - if (bluetoothGatt == null) { - return; - } - bluetoothGatt.close(); - bluetoothGatt = null; - } - // [END android_bluetooth_close_java] - // [END_EXCLUDE] -} -// [END android_bluetooth_binder_java] - -// [END android_bluetooth_service_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java deleted file mode 100644 index 92f891c12..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/BluetoothLeServiceSimple.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.java; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallback; -import android.bluetooth.BluetoothProfile; -import android.util.Log; - -public class BluetoothLeServiceSimple { - private static final String TAG = "BluetoothLeService"; - private static BluetoothAdapter bluetoothAdapter; - - // [START android_bluetooth_connect_simple_java] - public boolean connect(final String address) { - if (bluetoothAdapter == null || address == null) { - Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); - return false; - } - try { - final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); - } catch (IllegalArgumentException exception) { - Log.w(TAG, "Device not found with provided address."); - return false; - } - // connect to the GATT server on the device - return true; - } - // [END android_bluetooth_connect_simple_java] - - // [START android_bluetooth_callback_simple_java] - private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server - } - } - }; - // [END android_bluetooth_callback_simple_java] -} diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java deleted file mode 100644 index c92e3f62d..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivity.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.java; - -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.IBinder; -import android.util.Log; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import android.Manifest; -import com.sample.android.bluetoothle.R; -import com.sample.android.bluetoothle.java.BluetoothLeService.LocalBinder; -import androidx.annotation.RequiresPermission; - -// [START android_bluetooth_activity_all_java] -// [START android_bluetooth_bind_service_java] -public class DeviceControlActivity extends AppCompatActivity { - - private static final String TAG = "DeviceControlActivity"; - - private BluetoothLeService bluetoothService; - private String deviceAddress; - private boolean connected = false; - // [START_EXCLUDE silent] - - // [END_EXCLUDE] - private final ServiceConnection serviceConnection = new ServiceConnection() { - @Override - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public void onServiceConnected(ComponentName name, IBinder service) { - bluetoothService = ((LocalBinder) service).getService(); - if (bluetoothService != null) { - // [START android_bluetooth_initialize_activity_java] - if (!bluetoothService.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth"); - finish(); - return; - } - // [END android_bluetooth_initialize_activity_java] - // perform device connection - // [START android_bluetooth_connect_activity_java] - bluetoothService.connect(deviceAddress); - // [END android_bluetooth_connect_activity_java] - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - bluetoothService = null; - } - }; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.gatt_services_characteristics); - - deviceAddress = getIntent().getStringExtra("EXTRAS_DEVICE_ADDRESS"); - - Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); - bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); - } - // [START_EXCLUDE silent] - - // [START android_bluetooth_update_receiver_java] - private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { - connected = true; - updateConnectionState(R.string.connected); - } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { - connected = false; - updateConnectionState(R.string.disconnected); - } - } - }; - - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - @Override - protected void onResume() { - super.onResume(); - - registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()); - if (bluetoothService != null) { - final boolean result = bluetoothService.connect(deviceAddress); - Log.d(TAG, "Connect request result=" + result); - } - } - - @Override - protected void onPause() { - super.onPause(); - unregisterReceiver(gattUpdateReceiver); - } - private static IntentFilter makeGattUpdateIntentFilter() { - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); - return intentFilter; - } - // [END android_bluetooth_update_receiver_java] - - - private void updateConnectionState(int resourceId) { - // Placeholder implementation - } - // [END_EXCLUDE] -} -// [END android_bluetooth_bind_service_java] -// [END android_bluetooth_activity_all_java] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java b/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java deleted file mode 100644 index b619781de..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/java/DeviceControlActivitySimple.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.java; - -import android.content.ComponentName; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.util.Log; -import com.sample.android.bluetoothle.java.BluetoothLeService.LocalBinder; - -public class DeviceControlActivitySimple { - private static final String TAG = "DeviceControlActivity"; - private static BluetoothLeService bluetoothService; - private static void finish() {} - - // [START android_bluetooth_service_connection_simple_java] - // [START_EXCLUDE silent] - private class SimplifiedServiceConnection { - private final ServiceConnection serviceConnection = new ServiceConnection() { - // [END_EXCLUDE] - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - bluetoothService = ((LocalBinder) service).getService(); - if (bluetoothService != null) { - // call functions on service to check connection and connect to devices - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - bluetoothService = null; - } - // [START_EXCLUDE silent] - }; - } - // [END_EXCLUDE] - // [END android_bluetooth_service_connection_simple_java] - - // [START android_bluetooth_service_connection_initialize_java] - // [START_EXCLUDE silent] - private class InitializeServiceConnection { - private final ServiceConnection serviceConnection = new ServiceConnection() { - // [END_EXCLUDE] - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - bluetoothService = ((LocalBinder) service).getService(); - if (bluetoothService != null) { - if (!bluetoothService.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth"); - finish(); - } - // perform device connection - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - bluetoothService = null; - } - // [START_EXCLUDE silent] - }; - } - // [END_EXCLUDE] - // [END android_bluetooth_service_connection_initialize_java] -} diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 1260d6d43..341998837 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -42,6 +42,7 @@ class BluetoothLeService : Service() { private var connectionState = STATE_DISCONNECTED // [START android_bluetooth_callback] + // In BluetoothLeService companion object { const val ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED" @@ -85,6 +86,7 @@ class BluetoothLeService : Service() { // [START_EXCLUDE silent] // [START android_bluetooth_initialize] + // In BluetoothLeService fun initialize(): Boolean { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() if (bluetoothAdapter == null) { @@ -97,6 +99,7 @@ class BluetoothLeService : Service() { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [START android_bluetooth_connect] + // In BluetoothLeService fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { @@ -126,6 +129,7 @@ class BluetoothLeService : Service() { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) // [START android_bluetooth_close] + // In BluetoothLeService override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt index a0e231070..7bfc884c3 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt @@ -26,6 +26,7 @@ private var bluetoothAdapter: BluetoothAdapter? = null private const val TAG = "BluetoothLeService" // [START android_bluetooth_connect_simple] +// In BluetoothLeService fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { @@ -44,6 +45,7 @@ fun connect(address: String): Boolean { // [END android_bluetooth_connect_simple] // [START android_bluetooth_callback_simple] +// In BluetoothLeService val bluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index 07de113cd..f03729a3a 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -38,11 +38,11 @@ private const val TAG = "DeviceControlActivity" class DeviceControlActivity : AppCompatActivity() { private var bluetoothService: BluetoothLeService? = null + // [START_EXCLUDE silent] private var deviceAddress: String? = null private var connected = false - // [START_EXCLUDE silent] - // [END_EXCLUDE] + // [START android_bluetooth_service_connection] // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) @@ -52,6 +52,8 @@ class DeviceControlActivity : AppCompatActivity() { ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> + // call functions on service to check connection and connect to devices + // [START_EXCLUDE silent] // [START android_bluetooth_initialize_activity] if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") @@ -63,6 +65,7 @@ class DeviceControlActivity : AppCompatActivity() { // [START android_bluetooth_connect_activity] bluetooth.connect(deviceAddress!!) // [END android_bluetooth_connect_activity] + // [END_EXCLUDE] } } @@ -70,12 +73,15 @@ class DeviceControlActivity : AppCompatActivity() { bluetoothService = null } } + // [END android_bluetooth_service_connection] override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.gatt_services_characteristics) + // [START_EXCLUDE silent] deviceAddress = intent.getStringExtra("EXTRAS_DEVICE_ADDRESS") + // [END_EXCLUDE] val gattServiceIntent = Intent(this, BluetoothLeService::class.java) bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) @@ -83,6 +89,7 @@ class DeviceControlActivity : AppCompatActivity() { // [START_EXCLUDE silent] // [START android_bluetooth_update_receiver] + // In DeviceControlActivity private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt index c5a847fff..47a99c33e 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt @@ -16,14 +16,17 @@ package com.sample.android.bluetoothle.kotlin +import android.Manifest import android.content.ComponentName import android.content.ServiceConnection import android.os.IBinder import android.util.Log +import androidx.annotation.RequiresPermission import com.sample.android.bluetoothle.kotlin.BluetoothLeService.LocalBinder // Placeholders for compilation private var bluetoothService: BluetoothLeService? = null +private var deviceAddress: String? = null private const val TAG = "DeviceControlActivity" private fun finish() {} @@ -56,8 +59,9 @@ class DeviceControlActivitySimple { // [START android_bluetooth_service_connection_initialize] // [START_EXCLUDE silent] private inner class InitializeServiceConnection { + // [END_EXCLUDE] + // In DeviceControlActivity val serviceConnection: ServiceConnection = object : ServiceConnection { - // [END_EXCLUDE] override fun onServiceConnected( componentName: ComponentName, service: IBinder @@ -75,9 +79,40 @@ class DeviceControlActivitySimple { override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } - // [START_EXCLUDE silent] } + // [START_EXCLUDE silent] } // [END_EXCLUDE] // [END android_bluetooth_service_connection_initialize] + + // [START android_bluetooth_service_connection_connect] + // [START_EXCLUDE silent] + private inner class ConnectServiceConnection { + // [END_EXCLUDE] + // In DeviceControlActivity + val serviceConnection: ServiceConnection = object : ServiceConnection { + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + if (!bluetooth.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth") + finish() + } + // perform device connection + bluetooth.connect(deviceAddress!!) + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + } + // [START_EXCLUDE silent] + } + // [END_EXCLUDE] + // [END android_bluetooth_service_connection_connect] } From 0ef03844c74aa1ce8f585f8df814393303a083ab Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Thu, 11 Jun 2026 16:30:31 +0100 Subject: [PATCH 15/21] style: format and clean up bluetooth snippets --- .../bluetoothle/kotlin/DeviceControlActivity.kt | 16 ++++++++-------- .../kotlin/DeviceControlActivitySimple.kt | 4 +++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index f03729a3a..c6de1b090 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -45,7 +45,9 @@ class DeviceControlActivity : AppCompatActivity() { // [START android_bluetooth_service_connection] // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] override fun onServiceConnected( componentName: ComponentName, service: IBinder @@ -54,17 +56,13 @@ class DeviceControlActivity : AppCompatActivity() { bluetoothService?.let { bluetooth -> // call functions on service to check connection and connect to devices // [START_EXCLUDE silent] - // [START android_bluetooth_initialize_activity] if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() return@let } - // [END android_bluetooth_initialize_activity] // perform device connection - // [START android_bluetooth_connect_activity] - bluetooth.connect(deviceAddress!!) - // [END android_bluetooth_connect_activity] + deviceAddress?.let { bluetooth.connect(it) } // [END_EXCLUDE] } } @@ -109,9 +107,11 @@ class DeviceControlActivity : AppCompatActivity() { override fun onResume() { super.onResume() registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) - if (bluetoothService != null) { - val result = bluetoothService!!.connect(deviceAddress!!) - Log.d(TAG, "Connect request result=$result") + bluetoothService?.let { service -> + deviceAddress?.let { address -> + val result = service.connect(address) + Log.d(TAG, "Connect request result=$result") + } } } diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt index 47a99c33e..747b1b75a 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt @@ -91,7 +91,9 @@ class DeviceControlActivitySimple { // [END_EXCLUDE] // In DeviceControlActivity val serviceConnection: ServiceConnection = object : ServiceConnection { + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] override fun onServiceConnected( componentName: ComponentName, service: IBinder @@ -103,7 +105,7 @@ class DeviceControlActivitySimple { finish() } // perform device connection - bluetooth.connect(deviceAddress!!) + deviceAddress?.let { bluetooth.connect(it) } } } From 124f23be69edb7718e3061a39387fdfb1119a047 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Thu, 11 Jun 2026 16:35:17 +0100 Subject: [PATCH 16/21] refactor(bluetooth): remove service discovery and hide annotations --- .../bluetoothle/kotlin/BluetoothLeService.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 341998837..d4eab60e6 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -54,20 +54,18 @@ class BluetoothLeService : Service() { } private val bluetoothGattCallback = object : BluetoothGattCallback() { + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { - val intentAction: String if (newState == BluetoothProfile.STATE_CONNECTED) { - intentAction = ACTION_GATT_CONNECTED + // successfully connected to the GATT Server connectionState = STATE_CONNECTED - broadcastUpdate(intentAction) - Log.i(TAG, "Connected to GATT server.") - Log.i(TAG, "Attempting to start service discovery: " + bluetoothGatt?.discoverServices()) + broadcastUpdate(ACTION_GATT_CONNECTED) } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - intentAction = ACTION_GATT_DISCONNECTED + // disconnected from the GATT Server connectionState = STATE_DISCONNECTED - Log.i(TAG, "Disconnected from GATT server.") - broadcastUpdate(intentAction) + broadcastUpdate(ACTION_GATT_DISCONNECTED) } } } @@ -135,7 +133,9 @@ class BluetoothLeService : Service() { return super.onUnbind(intent) } + // [START_EXCLUDE silent] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] private fun close() { bluetoothGatt?.let { gatt -> gatt.close() From 7a58235dbe03237bb37a7d1d8a9fd6467b81a5af Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Thu, 11 Jun 2026 16:37:14 +0100 Subject: [PATCH 17/21] refactor(bluetooth): remove unnecessary permission annotations --- .../sample/android/bluetoothle/kotlin/BluetoothLeService.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index d4eab60e6..85773d289 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -54,9 +54,6 @@ class BluetoothLeService : Service() { } private val bluetoothGattCallback = object : BluetoothGattCallback() { - // [START_EXCLUDE silent] - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server @@ -133,9 +130,6 @@ class BluetoothLeService : Service() { return super.onUnbind(intent) } - // [START_EXCLUDE silent] - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] private fun close() { bluetoothGatt?.let { gatt -> gatt.close() From 252479f484b87407e6fa8951111add9e0f2b87fe Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Thu, 11 Jun 2026 17:32:34 +0100 Subject: [PATCH 18/21] fix(bluetoothle): fix Lint MissingPermission error on gatt.close() using @SuppressLint --- .../com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 85773d289..09b80e1c4 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -16,6 +16,7 @@ package com.sample.android.bluetoothle.kotlin +import android.annotation.SuppressLint import android.Manifest import android.app.Service import android.bluetooth.BluetoothAdapter @@ -30,6 +31,7 @@ import androidx.annotation.RequiresPermission private const val TAG = "BluetoothLeService" +@SuppressLint("MissingPermission") // [START android_bluetooth_service_all] // [START android_bluetooth_binder] class BluetoothLeService : Service() { From c536cec1c776654e7ff3673f2cc8b5e93ae66ed8 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Thu, 11 Jun 2026 17:46:56 +0100 Subject: [PATCH 19/21] style(bluetoothle): fix spotless formatting (import order) in BluetoothLeService.kt --- .../com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 09b80e1c4..42da683c6 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -16,8 +16,8 @@ package com.sample.android.bluetoothle.kotlin -import android.annotation.SuppressLint import android.Manifest +import android.annotation.SuppressLint import android.app.Service import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothGatt From 416bee94d8f4bb110a1e923c601f58c8907acefc Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Tue, 16 Jun 2026 22:44:12 +0100 Subject: [PATCH 20/21] refactor(ble): namespace progressive snippets Consolidate progressive Bluetooth LE snippets using the private object wrapper namespacing pattern: - Move all 13 progressive snippets into BluetoothLeService.kt and DeviceControlActivity.kt using local dummy wrapper objects. - Remove redundant BluetoothLeServiceSimple.kt and DeviceControlActivitySimple.kt files. - Ensure all namespaced code compiles and passes Spotless formatting. - Audit snippets to ensure 100% structural fidelity with DevSite. --- .../bluetoothle/kotlin/BluetoothLeService.kt | 207 ++++++++++++++++-- .../kotlin/BluetoothLeServiceSimple.kt | 58 ----- .../kotlin/DeviceControlActivity.kt | 162 +++++++++++++- .../kotlin/DeviceControlActivitySimple.kt | 120 ---------- 4 files changed, 353 insertions(+), 194 deletions(-) delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt delete mode 100644 bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index 42da683c6..b1b0d3afc 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -20,6 +20,7 @@ import android.Manifest import android.annotation.SuppressLint import android.app.Service import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothProfile @@ -43,8 +44,6 @@ class BluetoothLeService : Service() { private var bluetoothGatt: BluetoothGatt? = null private var connectionState = STATE_DISCONNECTED - // [START android_bluetooth_callback] - // In BluetoothLeService companion object { const val ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED" @@ -68,7 +67,6 @@ class BluetoothLeService : Service() { } } } - // [END android_bluetooth_callback] // [END_EXCLUDE] override fun onBind(intent: Intent): IBinder? { @@ -82,7 +80,6 @@ class BluetoothLeService : Service() { } // [START_EXCLUDE silent] - // [START android_bluetooth_initialize] // In BluetoothLeService fun initialize(): Boolean { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() @@ -92,19 +89,14 @@ class BluetoothLeService : Service() { } return true } - // [END android_bluetooth_initialize] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [START android_bluetooth_connect] - // In BluetoothLeService fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { val device = adapter.getRemoteDevice(address) // connect to the GATT server on the device - // [START android_bluetooth_connect_gatt] bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) - // [END android_bluetooth_connect_gatt] return true } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address. Unable to connect.") @@ -115,7 +107,6 @@ class BluetoothLeService : Service() { return false } } - // [END android_bluetooth_connect] // [START android_bluetooth_broadcast] private fun broadcastUpdate(action: String) { @@ -125,8 +116,6 @@ class BluetoothLeService : Service() { // [END android_bluetooth_broadcast] @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [START android_bluetooth_close] - // In BluetoothLeService override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) @@ -138,9 +127,201 @@ class BluetoothLeService : Service() { bluetoothGatt = null } } - // [END android_bluetooth_close] // [END_EXCLUDE] } // [END android_bluetooth_binder] // [END android_bluetooth_service_all] + +/** + * Namespaces for simplified versions of BluetoothLeService to match documentation. + */ +private object ConnectSimpleNamespace { + class BluetoothLeService { + private var bluetoothAdapter: BluetoothAdapter? = null + private val TAG = "BluetoothLeService" + + // [START android_bluetooth_connect_simple] + fun connect(address: String): Boolean { + bluetoothAdapter?.let { adapter -> + try { + val device = adapter.getRemoteDevice(address) + } catch (exception: IllegalArgumentException) { + Log.w(TAG, "Device not found with provided address.") + return false + } + // connect to the GATT server on the device + return true + } ?: run { + Log.w(TAG, "BluetoothAdapter not initialized") + return false + } + } + // [END android_bluetooth_connect_simple] + } +} + +private object CallbackSimpleNamespace { + class BluetoothLeService { + // [START android_bluetooth_callback_simple] + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + } + } + } + // [END android_bluetooth_callback_simple] + } +} + +private object InitializeNamespace { + // [START android_bluetooth_initialize] + private const val TAG = "BluetoothLeService" + + class BluetoothLeService : Service() { + + private var bluetoothAdapter: BluetoothAdapter? = null + + fun initialize(): Boolean { + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() + if (bluetoothAdapter == null) { + Log.e(TAG, "Unable to obtain a BluetoothAdapter.") + return false + } + return true + } + + // [START_EXCLUDE] + override fun onBind(intent: Intent): IBinder? { + return null + } + // [END_EXCLUDE] + } + // [END android_bluetooth_initialize] +} + +private object ConnectGattNamespace { + class BluetoothLeService : Service() { + // [START android_bluetooth_connect_gatt] + // [START_EXCLUDE silent] + fun dummy(device: BluetoothDevice, bluetoothGattCallback: BluetoothGattCallback) { + // [END_EXCLUDE] + var bluetoothGatt: BluetoothGatt? = null + // ... + bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) + // [START_EXCLUDE silent] + } + // [END_EXCLUDE] + // [END android_bluetooth_connect_gatt] + override fun onBind(intent: Intent): IBinder? = null + } +} + +private object ConnectNamespace { + // [START android_bluetooth_connect] + class BluetoothLeService : Service() { + + // [START_EXCLUDE silent] + private var bluetoothAdapter: BluetoothAdapter? = null + private val bluetoothGattCallback = object : BluetoothGattCallback() {} + private val TAG = "BluetoothLeService" + // [END_EXCLUDE] + // [START_EXCLUDE] + override fun onBind(intent: Intent): IBinder? = null + fun dummy() {} + // [END_EXCLUDE] + private var bluetoothGatt: BluetoothGatt? = null + + // [START_EXCLUDE] + fun dummy2() {} + // [END_EXCLUDE] + fun connect(address: String): Boolean { + bluetoothAdapter?.let { adapter -> + try { + val device = adapter.getRemoteDevice(address) + // connect to the GATT server on the device + bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) + return true + } catch (exception: IllegalArgumentException) { + Log.w(TAG, "Device not found with provided address. Unable to connect.") + return false + } + } ?: run { + Log.w(TAG, "BluetoothAdapter not initialized") + return false + } + } + } + // [END android_bluetooth_connect] +} + +private object CallbackNamespace { + // [START android_bluetooth_callback] + class BluetoothLeService : Service() { + + // [START_EXCLUDE silent] + private val binder = LocalBinder() + private var bluetoothAdapter: BluetoothAdapter? = null + private var bluetoothGatt: BluetoothGatt? = null + private fun broadcastUpdate(action: String) {} + override fun onBind(intent: Intent): IBinder? = null + inner class LocalBinder : Binder() + // [END_EXCLUDE] + private var connectionState = STATE_DISCONNECTED + + private val bluetoothGattCallback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { + if (newState == BluetoothProfile.STATE_CONNECTED) { + // successfully connected to the GATT Server + connectionState = STATE_CONNECTED + broadcastUpdate(ACTION_GATT_CONNECTED) + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + // disconnected from the GATT Server + connectionState = STATE_DISCONNECTED + broadcastUpdate(ACTION_GATT_DISCONNECTED) + } + } + } + + // [START_EXCLUDE] + fun dummy() {} + // [END_EXCLUDE] + companion object { + const val ACTION_GATT_CONNECTED = + "com.example.bluetooth.le.ACTION_GATT_CONNECTED" + const val ACTION_GATT_DISCONNECTED = + "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" + + private const val STATE_DISCONNECTED = 0 + private const val STATE_CONNECTED = 2 + } + } + // [END android_bluetooth_callback] +} + +private object CloseNamespace { + // [START android_bluetooth_close] + class BluetoothLeService : Service() { + + // [START_EXCLUDE] + private var bluetoothGatt: BluetoothGatt? = null + override fun onBind(intent: Intent): IBinder? = null + // [END_EXCLUDE] + override fun onUnbind(intent: Intent?): Boolean { + close() + return super.onUnbind(intent) + } + + private fun close() { + bluetoothGatt?.let { gatt -> + gatt.close() + bluetoothGatt = null + } + } + } + // [END android_bluetooth_close] +} + diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt deleted file mode 100644 index 7bfc884c3..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeServiceSimple.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.kotlin - -import android.bluetooth.BluetoothAdapter -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCallback -import android.bluetooth.BluetoothProfile -import android.util.Log - -private var bluetoothAdapter: BluetoothAdapter? = null -private const val TAG = "BluetoothLeService" - -// [START android_bluetooth_connect_simple] -// In BluetoothLeService -fun connect(address: String): Boolean { - bluetoothAdapter?.let { adapter -> - try { - val device = adapter.getRemoteDevice(address) - } catch (exception: IllegalArgumentException) { - Log.w(TAG, "Device not found with provided address.") - return false - } - // connect to the GATT server on the device - return true - } ?: run { - Log.w(TAG, "BluetoothAdapter not initialized") - return false - } -} -// [END android_bluetooth_connect_simple] - -// [START android_bluetooth_callback_simple] -// In BluetoothLeService -val bluetoothGattCallback = object : BluetoothGattCallback() { - override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { - if (newState == BluetoothProfile.STATE_CONNECTED) { - // successfully connected to the GATT Server - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - // disconnected from the GATT Server - } - } -} -// [END android_bluetooth_callback_simple] diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index c6de1b090..0123f4c35 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -86,8 +86,6 @@ class DeviceControlActivity : AppCompatActivity() { } // [START_EXCLUDE silent] - // [START android_bluetooth_update_receiver] - // In DeviceControlActivity private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { @@ -125,7 +123,6 @@ class DeviceControlActivity : AppCompatActivity() { addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED) } } - // [END android_bluetooth_update_receiver] private fun updateConnectionState(resourceId: Int) { // Placeholder implementation @@ -134,3 +131,162 @@ class DeviceControlActivity : AppCompatActivity() { } // [END android_bluetooth_bind_service] // [END android_bluetooth_activity_all] + +/** + * Namespaces for progressive versions of ServiceConnection to match documentation. + */ +private object ServiceConnectionSimpleNamespace { + class DeviceControlActivity { + private var bluetoothService: BluetoothLeService? = null + + // [START android_bluetooth_service_connection_simple] + // Code to manage Service lifecycle. + val serviceConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + // call functions on service to check connection and connect to devices + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + } + // [END android_bluetooth_service_connection_simple] + } +} + +private object ServiceConnectionInitializeNamespace { + // [START android_bluetooth_service_connection_initialize] + class DeviceControlActivity : AppCompatActivity() { + + // [START_EXCLUDE silent] + private var bluetoothService: BluetoothLeService? = null + private val TAG = "DeviceControlActivity" + // [END_EXCLUDE] + // Code to manage Service lifecycle. + private val serviceConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + if (!bluetooth.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth") + finish() + } + // perform device connection + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + } + + // [START_EXCLUDE] + override fun onCreate(savedInstanceState: Bundle?) {} + // [END_EXCLUDE] + } + // [END android_bluetooth_service_connection_initialize] +} + +private object ServiceConnectionConnectNamespace { + class DeviceControlActivity : AppCompatActivity() { + + private var bluetoothService: BluetoothLeService? = null + private var deviceAddress: String? = null + private val TAG = "DeviceControlActivity" + + // [START android_bluetooth_service_connection_connect] + // Code to manage Service lifecycle. + private val serviceConnection: ServiceConnection = object : ServiceConnection { + // [START_EXCLUDE silent] + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] + override fun onServiceConnected( + componentName: ComponentName, + service: IBinder + ) { + bluetoothService = (service as LocalBinder).getService() + bluetoothService?.let { bluetooth -> + if (!bluetooth.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth") + finish() + } + // perform device connection + deviceAddress?.let { bluetooth.connect(it) } + } + } + + override fun onServiceDisconnected(componentName: ComponentName) { + bluetoothService = null + } + } + // [END android_bluetooth_service_connection_connect] + + override fun onCreate(savedInstanceState: Bundle?) {} + } +} + +private object UpdateReceiverNamespace { + // [START android_bluetooth_update_receiver] + class DeviceControlActivity : AppCompatActivity() { + + // [START_EXCLUDE silent] + private var bluetoothService: BluetoothLeService? = null + private var deviceAddress: String? = null + private var connected = false + private val TAG = "DeviceControlActivity" + private fun updateConnectionState(resourceId: Int) {} + override fun onCreate(savedInstanceState: Bundle?) {} + // [END_EXCLUDE] + private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { + BluetoothLeService.ACTION_GATT_CONNECTED -> { + connected = true + updateConnectionState(R.string.connected) + } + BluetoothLeService.ACTION_GATT_DISCONNECTED -> { + connected = false + updateConnectionState(R.string.disconnected) + } + } + } + } + + // [START_EXCLUDE silent] + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + // [END_EXCLUDE] + override fun onResume() { + super.onResume() + registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) + bluetoothService?.let { service -> + deviceAddress?.let { address -> + val result = service.connect(address) + Log.d(TAG, "Connect request result=$result") + } + } + } + + override fun onPause() { + super.onPause() + unregisterReceiver(gattUpdateReceiver) + } + + private fun makeGattUpdateIntentFilter(): IntentFilter { + return IntentFilter().apply { + addAction(BluetoothLeService.ACTION_GATT_CONNECTED) + addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED) + } + } + } + // [END android_bluetooth_update_receiver] +} + diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt deleted file mode 100644 index 747b1b75a..000000000 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivitySimple.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2026 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 - * - * https://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 com.sample.android.bluetoothle.kotlin - -import android.Manifest -import android.content.ComponentName -import android.content.ServiceConnection -import android.os.IBinder -import android.util.Log -import androidx.annotation.RequiresPermission -import com.sample.android.bluetoothle.kotlin.BluetoothLeService.LocalBinder - -// Placeholders for compilation -private var bluetoothService: BluetoothLeService? = null -private var deviceAddress: String? = null -private const val TAG = "DeviceControlActivity" -private fun finish() {} - -class DeviceControlActivitySimple { - - // [START android_bluetooth_service_connection_simple] - // [START_EXCLUDE silent] - private inner class SimplifiedServiceConnection { - val serviceConnection: ServiceConnection = object : ServiceConnection { - // [END_EXCLUDE] - override fun onServiceConnected( - componentName: ComponentName, - service: IBinder - ) { - bluetoothService = (service as LocalBinder).getService() - bluetoothService?.let { bluetooth -> - // call functions on service to check connection and connect to devices - } - } - - override fun onServiceDisconnected(componentName: ComponentName) { - bluetoothService = null - } - // [START_EXCLUDE silent] - } - } - // [END_EXCLUDE] - // [END android_bluetooth_service_connection_simple] - - // [START android_bluetooth_service_connection_initialize] - // [START_EXCLUDE silent] - private inner class InitializeServiceConnection { - // [END_EXCLUDE] - // In DeviceControlActivity - val serviceConnection: ServiceConnection = object : ServiceConnection { - override fun onServiceConnected( - componentName: ComponentName, - service: IBinder - ) { - bluetoothService = (service as LocalBinder).getService() - bluetoothService?.let { bluetooth -> - if (!bluetooth.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth") - finish() - } - // perform device connection - } - } - - override fun onServiceDisconnected(componentName: ComponentName) { - bluetoothService = null - } - } - // [START_EXCLUDE silent] - } - // [END_EXCLUDE] - // [END android_bluetooth_service_connection_initialize] - - // [START android_bluetooth_service_connection_connect] - // [START_EXCLUDE silent] - private inner class ConnectServiceConnection { - // [END_EXCLUDE] - // In DeviceControlActivity - val serviceConnection: ServiceConnection = object : ServiceConnection { - // [START_EXCLUDE silent] - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - // [END_EXCLUDE] - override fun onServiceConnected( - componentName: ComponentName, - service: IBinder - ) { - bluetoothService = (service as LocalBinder).getService() - bluetoothService?.let { bluetooth -> - if (!bluetooth.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth") - finish() - } - // perform device connection - deviceAddress?.let { bluetooth.connect(it) } - } - } - - override fun onServiceDisconnected(componentName: ComponentName) { - bluetoothService = null - } - } - // [START_EXCLUDE silent] - } - // [END_EXCLUDE] - // [END android_bluetooth_service_connection_connect] -} From 9b6d0dfd15476f8d6d8fa57c541a3915fa2982c6 Mon Sep 17 00:00:00 2001 From: Michael Stillwell Date: Tue, 16 Jun 2026 23:18:09 +0100 Subject: [PATCH 21/21] fix(ble): resolve Android Lint errors Resolve Android Lint failures in the progressive namespace wrappers: - Add @SuppressLint("MissingPermission") to all dummy namespaces to suppress permission warnings on compiled documentation fragments. - Add super.onCreate(savedInstanceState) to all blank onCreate overrides in dummy activities to satisfy MissingSuperCall checks. - Verify that both gradle build and lintDebug tasks now pass. --- .../bluetoothle/kotlin/BluetoothLeService.kt | 4 ++++ .../bluetoothle/kotlin/DeviceControlActivity.kt | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt index b1b0d3afc..b8d281d7b 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/BluetoothLeService.kt @@ -203,6 +203,7 @@ private object InitializeNamespace { // [END android_bluetooth_initialize] } +@SuppressLint("MissingPermission") private object ConnectGattNamespace { class BluetoothLeService : Service() { // [START android_bluetooth_connect_gatt] @@ -220,6 +221,7 @@ private object ConnectGattNamespace { } } +@SuppressLint("MissingPermission") private object ConnectNamespace { // [START android_bluetooth_connect] class BluetoothLeService : Service() { @@ -258,6 +260,7 @@ private object ConnectNamespace { // [END android_bluetooth_connect] } +@SuppressLint("MissingPermission") private object CallbackNamespace { // [START android_bluetooth_callback] class BluetoothLeService : Service() { @@ -302,6 +305,7 @@ private object CallbackNamespace { // [END android_bluetooth_callback] } +@SuppressLint("MissingPermission") private object CloseNamespace { // [START android_bluetooth_close] class BluetoothLeService : Service() { diff --git a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt index 0123f4c35..7c50d38fb 100644 --- a/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt +++ b/bluetoothle/src/main/java/com/sample/android/bluetoothle/kotlin/DeviceControlActivity.kt @@ -17,6 +17,7 @@ package com.sample.android.bluetoothle.kotlin import android.Manifest +import android.annotation.SuppressLint import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context @@ -190,12 +191,15 @@ private object ServiceConnectionInitializeNamespace { } // [START_EXCLUDE] - override fun onCreate(savedInstanceState: Bundle?) {} + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } // [END_EXCLUDE] } // [END android_bluetooth_service_connection_initialize] } +@SuppressLint("MissingPermission") private object ServiceConnectionConnectNamespace { class DeviceControlActivity : AppCompatActivity() { @@ -230,10 +234,13 @@ private object ServiceConnectionConnectNamespace { } // [END android_bluetooth_service_connection_connect] - override fun onCreate(savedInstanceState: Bundle?) {} + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } } } +@SuppressLint("MissingPermission") private object UpdateReceiverNamespace { // [START android_bluetooth_update_receiver] class DeviceControlActivity : AppCompatActivity() { @@ -244,7 +251,9 @@ private object UpdateReceiverNamespace { private var connected = false private val TAG = "DeviceControlActivity" private fun updateConnectionState(resourceId: Int) {} - override fun onCreate(savedInstanceState: Bundle?) {} + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } // [END_EXCLUDE] private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) {