Giter Site home page Giter Site logo

android-ble-made-easy's Introduction

BLE Made Easy

BLE on Android is verbose and hard. This library makes it easy to use.

Kotlin-first library providing the simplest way to connect to BLE devices and communicate with them.

API

How to install it?

  • Step 1. Add the JitPack repository to your project gradle file
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  • Step 1.1 Only if you have the file settings.gradle at your project root folder

    • Add the JitPack repository to your project settings.gradle file

      dependencyResolutionManagement {
          repositories {
              ...
              maven { url 'https://jitpack.io' }
          }
      }
    • Add the JitPack repository to your project gradle file

      buildscript {
          repositories {
              ...
              maven { url 'https://jitpack.io' }
          }
      }
  • Step 2. Add the implementation dependency to your app gradle file

dependencies {
    ...

    implementation 'com.github.LeandroSQ:android-ble-made-easy:1.9.2'

    ...
}
  • Step 3. Gradle sync

  • Step 4. Add these permissions to your manifest.xml file

<uses-permission
    android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission
    android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />

<!-- These 2 bellow, only if you are targeting Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
    tools:targetApi="s" />
<uses-permission
    android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation"
    tools:targetApi="s" />

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Only if you are targeting Android 10+ -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Only if you are targeting Android 10+ and pretend to use BLE in a Foreground or Background Service -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<uses-feature android:name="android.hardware.bluetooth" android:required="true"/>

And you are ready to go!


How to use it?

Permissions and hardware

The library contains helper functions to handle permission and hardware requirements. You can use them to verify if the user has granted the permissions and if the hardware is available.

Permissions request

Asynchronous:

ble.verifyPermissionsAsync(
    rationaleRequestCallback = { next ->
        // Include your code to show an Alert or UI explaining why the permissions are required
        // Calling the function bellow if the user agrees to give the permissions
        next()
    },
    callback = { granted ->
        if (granted) {
            // Continue your code....
        } else {
            // Include your code to show an Alert or UI indicating that the permissions are required
        }
    }
)

Or with coroutines:

GlobalScope.launch {
    val granted = ble.verifyPermissions(
        rationaleRequestCallback = { next ->
            // Include your code to show an Alert or UI explaining why the permissions are required
            // Calling the function bellow if the user agrees to give the permissions
            next()
        }
    )

    if (granted) {
        // Continue your code....
    } else {
        // Include your code to show an Alert or UI indicating that the permissions are required
    }
}

Bluetooth hardware activation

Asynchronous:

ble.verifyBluetoothAdapterStateAsync { active ->
    if (active) {
        // Continue your code...
    } else {
        // Include your code to show an Alert or UI indicating that the Bluetooth adapter is required to be on in order to your project work
    }
}

Or with coroutines:

GlobalScope.launch {
    if (ble.verifyBluetoothAdapterState()) {
        // Continue your code...
    } else {
        // Include your code to show an Alert or UI indicating that the Bluetooth adapter is required to be on in order to your project work
    }
}

Location services activation

Asynchronous:

ble.verifyLocationStateAsync{ active ->
    if (active) {
        // Continue your code...
    } else {
        // Include your code to show an Alert or UI indicating that Location is required to be on in order to your project work
    }
}

Or with coroutines:

GlobalScope.launch {
    if (ble.verifyLocationState()) {
        // Continue your code...
    } else {
        // Include your code to show an Alert or UI indicating that Location is required to be on in order to your project work
    }
}

Create a BLE instance

For interacting with the library you need to create a BLE instance. You can do it in 3 different ways:

// For jetpack compose:
val ble = BLE(componentActivity = this)

// Or activities:
val ble = BLE(activity = this)

// Or fragments
val ble = BLE(fragment = this)

Fast scan for specific devices

If you already know the device you wanna connect to, you could use this:

Asynchronous:

ble.scanForAsync(
    // You only need to supply one of these, no need for all of them!
    macAddress = "00:00:00:00",
    name = "ESP32",
    service = "00000000-0000-0000-0000-000000000000",

    onFinish = { connection ->
        if (connection != null) {
            // And you can continue with your code
            it.write("00000000-0000-0000-0000-000000000000", "Testing")
        } else {
            // Show an Alert or UI with your preferred error message about the device not being available
        }
    },

    onError = { errorCode ->
        // Show an Alert or UI with your preferred error message about the error
    }
)

// It is important to keep in mind that every single one of the provided arguments of the function shown above, are optionals! Therefore, you can skip the ones that you don't need.

Or with coroutines:

GlobalScope.launch {
    // You can specify filters for your device, being them 'macAddress', 'service' and 'name'
    val connection = ble.scanFor(
        // You only need to supply one of these, no need for all of them!
        macAddress = "00:00:00:00",
        name = "ESP32",
        service = "00000000-0000-0000-0000-000000000000"
    )

    // And it will automatically connect to your device, no need to boilerplate
    if (connection != null) {
        // And you can continue with your code
        it.write("00000000-0000-0000-0000-000000000000", "Testing")
    } else {
        // Show an Alert or UI with your preferred error message about the device not being available
    }
}

Scan for nearby devices

Asynchronous:

ble.scanAsync(
    duration = 10000,

    /* This is optional, if you want to update your interface in realtime */
    onDiscover = { device ->
        // Update your UI with the newest found device, in real time
    },

    onFinish = { devices ->
        // Continue with your code handling all the devices found
    },
    onError = { errorCode ->
        // Show an Alert or UI with your preferred error message
    }
)

Or with coroutines:

GlobalScope.launch {
    try {
        // Continue with your code handling all the devices found
        val devices = ble.scan(duration = 10000)
    } catch (e: Exception) {
        // Show an Alert or UI with your preferred error message
    } catch (e: ScanFailureException) {
        // Show an Alert or UI with your preferred error message
    }
}

Or you could use the scan method without any timeout, only stopping it manually

ble.scanAsync(
    duration = 0, // Disables the timeout
    onDiscover = { device ->
        // Update your UI with the newest found device, in real time
    },
    onError = { errorCode ->
        // Show an Alert or UI with your preferred error message
    }
)

// Stops your scan manually
ble.stopScan()

Connecting to a discovered device

After a successful scan, you'll have your Bluetooth device to connect to it:

ble.connect(device)?.let { connection ->
    // Continue with your code
    val value = connection.read("00000000-0000-0000-0000-000000000000")
    connection.write("00000000-0000-0000-0000-000000000000", "0")
    connection.close()
}

You can also define a priority for the connection, useful for higher priority tasks, to ensure preferential treatment for the connection. The default priority is Priority.Balanced, other options are Priority.High and Priority.LowPower.

ble.connect(device, Priority.High)?.let { connection ->
    // Continue with your code
    val value = connection.read("00000000-0000-0000-0000-000000000000")
    connection.write("00000000-0000-0000-0000-000000000000", "0")
    connection.close()
}

Writing to a device

After a successful scan, you'll have your Bluetooth device

ble.connect(device)?.let { connection ->
connection.write(characteristic = "00000000-0000-0000-0000-000000000000", message = "Hello World", charset = Charsets.UTF_8)
connection.close()
}

Reading from a device

After a successful scan, you'll have your Bluetooth device There's a catch, reading cannot be done on synchronously, so just like other methods you will have two options read and readAsync

GlobalScope.launch {
    ble.connect(device)?.let { connection ->
        val value = connection.read(characteristic = "00000000-0000-0000-0000-000000000000")
        if (value != null) {
            // Do something with this value
        } else {
            // Show an Alert or UI with your preferred error message
        }
    }
}

Or you could use the read method with the 'async' prefix, providing a callback

ble.connect(device)?.let { connection ->
    connection.readAsync(characteristic = "00000000-0000-0000-0000-000000000000") { value
        if (value != null) {
            // Do something with this value
        } else {
            // Show an Alert or UI with your preferred error message
        }
    }
}

Observing changes

There are two ways to observe changes, the first is using the native BLE NOTIFY, which is the preferred option.

// If you want to make use of the NOTIFY functionality
ble.connect(device)?.let { connection ->

    // For watching bytes
    connection.observe(characteristic = "00000000-0000-0000-0000-000000000000") { value: ByteArray ->
        // This will run everytime the characteristic changes it's value
    }

    // For watching strings
    connection.observeString(characteristic = "00000000-0000-0000-0000-000000000000", charset = Charsets.UTF_8) { value: String ->
        // This will run everytime the characteristic changes it's value
    }
}

The second way is to manually read the characteristic in a fixed interval and compare with the last value. Which uses more battery, isn't as effective and should only be used when the characteristic doesn't provide the NOTIFY property. Fortunately the library handles both ways in a similar API.

// If you want to use NOTIFY when available and fallback to the legacy way when it isn't
ble.connect(device)?.let { connection ->
    connection.observe(
        characteristic = "00000000-0000-0000-0000-000000000000",
        owner = viewLifeCycleOwner, // The Lifecycle Owner to attach to
        interval = 1000L // The interval in ms (in this example 1 second)
    ) { value: ByteArray ->
        // This will run everytime the characteristic changes it's value
    }
}

MTU change request

For write operations that require more than the default 23 bytes, you can request a MTU change, by doing the following:

ble.connect(device)?.let { connection ->
    connection.requestMTU(bytes = 64)
    connection.write(characteristic = "00000000-0000-0000-0000-000000000000", message = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)) // Imagine a really long message here :)
    connection.close()
}

Forcing RSSI read

ble.connect(device)?.let { connection ->
    if (connection.readRSSI()) { // This will enqueue a RSSI request read
      // Which will be reflected on 
      Log.d("RSSI", connection.rssi)
    }

}

Sample App

This repository also provides a working Sample App which you can use as a reference.

You can clone the repo and run it on your device.


Why use it?

Battle tested

This library is battle tested in production apps, varying from industry, IoT and personal projects.

It uses a compilation of techniques and best practices to handle known issues, for instance the Issue 183108, where Lolipop devices will not work properly without a workaround. Or the well-known BLE 133 error! The nightmare of everyone who has ever worked with BLE on Android.

This library handles all of these issues for you, so you don't have to worry about it.

Lifecycle

This library is designed to work in Jetpack Compose, AndroidX and Android Support, also on Fragments, Activities and Services.

// For jetpack compose:
val ble = BLE(componentActivity = this)
// For activities:
val ble = BLE(activity = this)
// For fragments
val ble = BLE(fragment = this)

Permissions

This library handles all the permission requests for you, so you don't have to worry about it.

Hardware activation

The library handles the activation of the Bluetooth adapter hardware and the Location services, when required, so you don't have to worry about it.

Asynchronous and Coroutines

The library exposes asynchronous and coroutines methods for all the functions, so you can choose the one that fits better to your project.

Operation queue

All the operations, connections, reads and writes are queued, resulting in a more reliable and predictable behavior. When disconnecting, it will wait for the operations to finish before disconnecting, gracefully.

Device cache

The library caches the discovered devices, so you can connect to them without having to scan twice.

Older APIs

The library supports Android 5.0+ (API 21+), so you can use it in your projects.

Kotlin

From the beginning, this library was designed to be used in Kotlin and for Kotlin projects. Although it is theoretically possible to use it in Java projects, the main focus is on Kotlin.

Documentation

The library is fully documented, so you can easily understand how it works and how to use it.

You can take a look on the online documentation here.

Bytes and Strings

The library exposes read/write methods which converts the data to/from bytes and strings, so you don't have to worry about it.

Observers

The library exposes methods to observe changes in a characteristic, even when the NOTIFY property is not available.



Made with ย ย  by Leandro Quevedo.

android-ble-made-easy's People

Contributors

github-actions[bot] avatar leandrosq avatar marco012 avatar noelchew avatar valexi7 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-ble-made-easy's Issues

Manufacturer Data

Hi,

I need to read the advertising data from a beacon (sensor device), so no connection is allowed.
With this library I can read out from the advertising data the name, RSSI and MAC, but not the "manufacturer data" in which the beacon sends its user data.

`ble.scanAsync(
duration = 2000,
onDiscover = { device ->
Log.d("Device", "Name: ${device.name}, RSSI: ${device.rsii}, MAC: ${device.macAddress}, advertisingId: ${device.advertisingId}")
})

//There is no advertisingData:
//device.advertisingData`

  • Is it not possible to read the "manufacturer data" from a beacon with this library?

An example how manufacturer data look like:
image

react to events emitted from the ble device

Hi!

i just stumblep upon your ble lib. Seems very well done ,congratz! When i looked through the code i asked myself if the lib can already react to events that are emmitted from the ble device?

Read always return null while observe works as intended

Hi, I'm trying to use this to make a simple app to get heartbeat and step values from a mi band 6,

However when i use ble.scanforasync to find and connect to the device, i find that i could only get data from notifiable characteristics like heart rate whenever they changed value , but when i try to use connection.read() on characteristics that is readable like device name , i always gets a null, is there something i missed or something else i should do?

Unable to create main BLE instance

I'm unable to create the main ble instance by using the code in the readme:
val ble = BLE(activity = this)
Android Studio cannot find the class and prompts me to create one instead of importing your library

I have the library imported in my app build.gradle:
implementation 'com.github.LeandroSQ:android-ble-made-easy:v1.4.0'

and jitpack included in the maven repositories options:
maven { url 'https://jitpack.io' }

Your library looks super simple and easy to integrate, so I'm hoping I don't have to switch away simply because I can't get it to install

The scan has exceeded the time limit!

I assume that the error appears when the timeout has been reached. How can I filter this error message so that the app doesn't crash.
Unfortunately it doesn't work with "Try Cache".
It should keep trying to reconnect.

GlobalScope.launch {
            // You can specify filters for your device, being them 'macAddress', 'service' and 'name'
            while(true) {
                if (device0 == null || device0!!.isActive == false) {
                    Log.d("BLE_Connection:", "Versuche zu verbinden.")
                    try {
                        val connection = ble.scanFor(
                            // You only need to supply one of these, no need for all of them!
                            name = "EKIsWatch"
                        )
                        // And you can continue with your code
                        READUUID = connection!!.readableCharacteristics[0]
                        WRITEUUID = connection.writableCharacteristics[0]
                        NOTIFYUUID = connection.notifiableCharacteristics[0]
                        connection.write(WRITEUUID, "Testing")
                        device0 = connection
                        Log.d("BLE_Connection:", NOTIFYUUID)
                        delay(1000)
                        DeviceName = "EKIsWatch"
                        connection.observeString(
                            NOTIFYUUID,
                            charset = Charsets.UTF_8
                        ) { value: String ->

                            if (value.startsWith("N/1")) {
                                ReadyToReceive = true
                                Log.d("ReadyToReceive:", ReadyToReceive.toString())
                            }
                            Log.d("BLE_Notify:", value)

                            if (value.startsWith("N/D")) {
                                if (value[3] == 'W'){

                                }
                            }
                        }
                    }
                    catch (e:ScanFailureException){
                        Log.d("BLE_Connection:", e.toString())
                    }
                    finally
                    {

                    }
                    // And it will automatically connect to your device, no need to boilerplate
//                    if (connection != null) {
//
//                        // For watching strings
//                    } else {
//                        // Show an Alert or UI with your preferred error message about the device not being available
//                        Log.d("BLE_Connection:", "Keine Verbindung mรถglich")
//                        DeviceName = ""
//                        delay(1000)
//                    }
                }else {
                    Log.d("BLE_Connection:", "Keine Verbindung oder Inaktiv.")
                    delay(5000)
                }
            }
        }
E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-4
    Process: com.example.ekissmartwatch, PID: 15307
    quevedo.soares.leandro.blemadeeasy.exceptions.ScanTimeoutException: The scan has exceeded the time limit!
        at quevedo.soares.leandro.blemadeeasy.BLE$scanFor$2$1.invokeSuspend(BLE.kt:719)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:233)
        at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
        at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:595)
        at kotlinx.coroutines.android.HandlerContext$scheduleResumeAfterDelay$$inlined$Runnable$1.run(Runnable.kt:19)
        at android.os.Handler.handleCallback(Handler.java:942)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:210)
        at android.os.Looper.loop(Looper.java:299)
        at android.app.ActivityThread.main(ActivityThread.java:8252)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:559)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)
    	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@410c56e, Dispatchers.Default]
W/DefaultDispatch: type=1400 audit(0.0:59214): avc: denied { search } for name="miuilog" dev="dm-45" ino=448 scontext=u:r:untrusted_app:s0:c59,c257,c512,c768 tcontext=u:object_r:data_log_file:s0 tclass=dir permissive=0 app=com.example.ekissmartwatch
W/DefaultDispatch: type=1400 audit(0.0:59215): avc: denied { search } for name="miuilog" dev="dm-45" ino=448 scontext=u:r:untrusted_app:s0:c59,c257,c512,c768 tcontext=u:object_r:data_log_file:s0 tclass=dir permissive=0 app=com.example.ekissmartwatch
W/OOMEventManagerFK: Failed to mkdir /data/miuilog/stability/memleak/heapdump/
W/DefaultDispatch: type=1400 audit(0.0:59216): avc: denied { search } for name="miuilog" dev="dm-45" ino=448 scontext=u:r:untrusted_app:s0:c59,c257,c512,c768 tcontext=u:object_r:data_log_file:s0 tclass=dir permissive=0 app=com.example.ekissmartwatch
W/DefaultDispatch: type=1400 audit(0.0:59217): avc: denied { search } for name="miuilog" dev="dm-45" ino=448 scontext=u:r:untrusted_app:s0:c59,c257,c512,c768 tcontext=u:object_r:data_log_file:s0 tclass=dir permissive=0 app=com.example.ekissmartwatch
W/DefaultDispatch: type=1400 audit(0.0:59218): avc: denied { search } for name="miuilog" dev="dm-45" ino=448 scontext=u:r:untrusted_app:s0:c59,c257,c512,c768 tcontext=u:object_r:data_log_file:s0 tclass=dir permissive=0 app=com.example.ekissmartwatch
I/Process: Process is going to kill itself!
    java.lang.Exception
        at android.os.Process.killProcess(Process.java:1344)
        at com.android.internal.os.RuntimeInit$KillApplicationHandler.uncaughtException(RuntimeInit.java:181)
        at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)
        at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)
        at kotlinx.coroutines.internal.CoroutineExceptionHandlerImplKt.propagateExceptionFinalResort(CoroutineExceptionHandlerImpl.kt:35)
        at kotlinx.coroutines.internal.CoroutineExceptionHandlerImpl_commonKt.handleUncaughtCoroutineException(CoroutineExceptionHandlerImpl.common.kt:52)
        at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:32)
        at kotlinx.coroutines.StandaloneCoroutine.handleJobException(Builders.common.kt:194)
        at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:230)
        at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:909)
        at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:866)
        at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:831)
        at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

Characteristic not found on device when using the sample app

We have a custom Bluetooth module we want to read and write to. I am able to connect to it but when I tap on TOGGLE, I get "Current status: Information not sent". Investigating this revealed that the getCharacteristic method in BluetoothConnection returns null right after this message is logged: Characteristic 4ac8a682-9736-4e5d-932b-e9b31405049c not found on device C6:2F:1D:80:A7:8A!

4ac8a682-9736-4e5d-932b-e9b31405049c is the characteristic in the sample app. How can I find the right characteristic to use with my Bluetooth device?

Wrong dependencies ?

Hi,
very nice work, just a small issue I had, I might be wrong but I think that the dependencies in the documentation are wrong, adding a "v" seems solves, for example for version 1.4.0 :

implementation 'com.github.LeandroSQ:android-ble-made-easy:v1.4.0'

Duplicate Location Service Enabling Prompt on Android 12 Device

Prompt to enable location service is shown twice on Android 12 devices. Clicking "OK" twice will cause app to crash.

Steps to reproduce crash:

  1. Disable device Location Service
  2. Disable Bluetooth
  3. Kill app
  4. Start app
  5. Enable Bluetooth
  6. Click "OK" to enable Location Service
  7. Click "OK" to enable Location Service (again)

Logcat:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=598333496, result=-1, data=Intent { (has extras) }} to activity {quevedo.soares.leandro.blemadeeasy/quevedo.soares.leandro.blemadeeasy.view.MainActivity}: java.lang.IllegalStateException: Already resumed, but proposed with update true

No device scanned

2021-06-04 10:50:14.666 12002-12002/quevedo.soares.leandro.androideasyble D/MainActivity: Setting bluetooth manager up...
2021-06-04 10:50:14.684 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: Checking App bluetooth permissions...
2021-06-04 10:50:14.684 12002-12002/quevedo.soares.leandro.androideasyble I/HwPartMagicWindowFactory: add android.common.HwPartMagicWindowFactoryImpl to memory.
2021-06-04 10:50:14.684 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: All permissions granted!
2021-06-04 10:50:14.684 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: Checking bluetooth adapter state...
2021-06-04 10:50:14.685 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: Checking location services state...
2021-06-04 10:50:14.691 12002-12002/quevedo.soares.leandro.androideasyble I/HwPartPowerOfficeFactory: add HwPartPowerOfficeFactoryImpl to memory.
2021-06-04 10:50:14.697 12002-12002/quevedo.soares.leandro.androideasyble D/InputEventReceiver: dispatchInputInterval 1000000
2021-06-04 10:50:14.714 12002-12053/quevedo.soares.leandro.androideasyble D/HiTouch_PressGestureDetector: onAttached, package=quevedo.soares.leandro.androideasyble, windowType=1, mHiTouchRestricted=false
2021-06-04 10:50:14.721 12002-12047/quevedo.soares.leandro.androideasyble I/FrameFlow: FrameFlowInit app is not supported by frameflow solution
2021-06-04 10:50:14.721 12002-12047/quevedo.soares.leandro.androideasyble I/iGraphics: [0020080c] pn: quevedo.soares.leandro.androideasyble, p: 12002
2021-06-04 10:50:14.721 12002-12047/quevedo.soares.leandro.androideasyble I/iGraphics: [0030080c] no spt app: quevedo.soares.leandro.androideasyble
2021-06-04 10:50:14.729 12002-12047/quevedo.soares.leandro.androideasyble D/mali_winsys: EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, EGLBoolean) returns 0x3000
2021-06-04 10:50:14.732 12002-12047/quevedo.soares.leandro.androideasyble W/Gralloc3: mapper 3.x is not supported
2021-06-04 10:50:14.742 12002-12002/quevedo.soares.leandro.androideasyble I/HwViewRootImpl: removeInvalidNode jank list is null
2021-06-04 10:50:14.745 12002-12002/quevedo.soares.leandro.androideasyble I/RmeSchedManager: init Rme, version is: v1.0
2021-06-04 10:50:14.745 12002-12047/quevedo.soares.leandro.androideasyble D/OpenGLRenderer: disableOutlineDraw is true
2021-06-04 10:50:14.761 12002-12002/quevedo.soares.leandro.androideasyble D/DecorView: showOrHideHighlightView: hasFocus=true; winMode=1; isMrgNull=true
2021-06-04 10:50:14.761 12002-12002/quevedo.soares.leandro.androideasyble W/InputMethodManager: startInputReason = 1
2021-06-04 10:50:14.764 12002-12002/quevedo.soares.leandro.androideasyble W/InputMethodManager: startInputReason = 5
2021-06-04 10:50:14.777 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: Starting scan...
2021-06-04 10:50:14.777 12002-12050/quevedo.soares.leandro.androideasyble I/BluetoothAdapter: isLeEnabled(): ON
2021-06-04 10:50:14.779 12002-12046/quevedo.soares.leandro.androideasyble D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=9 mScannerId=0
2021-06-04 10:50:14.782 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: Scan timeout reached!
2021-06-04 10:50:19.523 12002-12002/quevedo.soares.leandro.androideasyble I/AwareBitmapCacher: init lrucache size: 2097152 pid=12002
2021-06-04 10:50:24.786 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: Stopping scan...
2021-06-04 10:50:24.787 12002-12050/quevedo.soares.leandro.androideasyble I/BluetoothAdapter: isLeEnabled(): ON
2021-06-04 10:50:24.790 12002-12050/quevedo.soares.leandro.androideasyble D/BluetoothMadeEasy: Scan finished! 0 devices found!

My Phone

MODEL: HUAWEI YAL-AL00
SYSTEM: Android 10 API 29

Possible issue/bug with new versions of Android Studio.

Hello,

I think there is an issue/bug with Android Studio new versions. When you add the implementations line:

implementation 'com.github.LeandroSQ:android-ble-made-easy:1.8.0'

you get this error:

2 files found with path 'META-INF/NOTICE.md'.
Adding a packagingOptions block may help, please refer to
https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
for more information

Execution failed for task ':app:mergeDebugJavaResource'.

A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
2 files found with path 'META-INF/NOTICE.md' from inputs:
- /Users/francesc/.gradle/caches/modules-2/files-2.1/jakarta.xml.bind/jakarta.xml.bind-api/2.3.2/8d49996a4338670764d7ca4b85a1c4ccf7fe665d/jakarta.xml.bind-api-2.3.2.jar
- /Users/francesc/.gradle/caches/modules-2/files-2.1/jakarta.activation/jakarta.activation-api/1.2.1/562a587face36ec7eff2db7f2fc95425c6602bc1/jakarta.activation-api-1.2.1.jar
Adding a packagingOptions block may help, please refer to
https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
for more information

  • Try:

Run with --info or --debug option to get more log output.
Run with --scan to get full insights.

  • Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugJavaResource'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:142)
    at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:140)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:128)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:69)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:327)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:314)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:307)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:293)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:417)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:339)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
    at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:339)
    at org.gradle.internal.work.DefaultAsyncWorkTracker.lambda$waitForItemsAndGatherFailures$2(DefaultAsyncWorkTracker.java:130)
    at org.gradle.internal.Factories$1.create(Factories.java:31)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:321)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:304)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLock(DefaultWorkerLeaseService.java:309)
    at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:126)
    at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:92)
    at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:78)
    at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:66)
    at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:244)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
    at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:221)
    at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:204)
    at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:187)
    at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:165)
    at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
    at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
    at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
    at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:50)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:40)
    at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
    at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
    at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
    at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
    at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
    at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
    at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
    at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.executeDelegateBroadcastingChanges(CaptureStateAfterExecutionStep.java:124)
    at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:80)
    at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:58)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
    at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:181)
    at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:71)
    at org.gradle.internal.Either$Right.fold(Either.java:175)
    at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:59)
    at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:69)
    at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:47)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:36)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:25)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:110)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:56)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:56)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:73)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:44)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:89)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:50)
    at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:114)
    at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:57)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:76)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:50)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.executeWithNoEmptySources(SkipEmptyWorkStep.java:254)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:91)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:56)
    at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:32)
    at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:21)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
    at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:43)
    at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:31)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
    at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:281)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
    at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
    at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
    at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
    at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:33)
    at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:139)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:128)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:69)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:327)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:314)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:307)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:293)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:417)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:339)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    Caused by: com.android.builder.merge.DuplicateRelativeFileException: 2 files found with path 'META-INF/NOTICE.md' from inputs:
  • /Users/francesc/.gradle/caches/modules-2/files-2.1/jakarta.xml.bind/jakarta.xml.bind-api/2.3.2/8d49996a4338670764d7ca4b85a1c4ccf7fe665d/jakarta.xml.bind-api-2.3.2.jar
  • /Users/francesc/.gradle/caches/modules-2/files-2.1/jakarta.activation/jakarta.activation-api/1.2.1/562a587face36ec7eff2db7f2fc95425c6602bc1/jakarta.activation-api-1.2.1.jar
    Adding a packagingOptions block may help, please refer to
    https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
    for more information
    at com.android.builder.merge.IncrementalFileMergerOutputs$1.create(IncrementalFileMergerOutputs.java:93)
    at com.android.builder.merge.DelegateIncrementalFileMergerOutput.create(DelegateIncrementalFileMergerOutput.java:65)
    at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate$run$output$1.create(MergeJavaResourcesDelegate.kt:179)
    at com.android.builder.merge.IncrementalFileMerger.updateChangedFile(IncrementalFileMerger.java:252)
    at com.android.builder.merge.IncrementalFileMerger.mergeChangedInputs(IncrementalFileMerger.java:213)
    at com.android.builder.merge.IncrementalFileMerger.merge(IncrementalFileMerger.java:90)
    at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate.run(MergeJavaResourcesDelegate.kt:225)
    at com.android.build.gradle.internal.tasks.MergeJavaResWorkAction.run(MergeJavaResWorkAction.kt:88)
    at com.android.build.gradle.internal.profile.ProfileAwareWorkAction.execute(ProfileAwareWorkAction.kt:74)
    at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
    at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
    at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
    at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
    at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
    at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
    at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
    at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
    at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
    at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$2(DefaultWorkerExecutor.java:205)
    at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187)
    at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120)
    at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162)
    at org.gradle.internal.Factories$1.create(Factories.java:31)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:249)
    at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:109)
    at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:114)
    at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157)
    at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126)
    ... 2 more
    Caused by: com.android.builder.merge.DuplicateRelativeFileException: 2 files found with path 'META-INF/NOTICE.md'.
    Adding a packagingOptions block may help, please refer to
    https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
    for more information
    at com.android.builder.merge.StreamMergeAlgorithms.lambda$acceptOnlyOne$2(StreamMergeAlgorithms.java:75)
    at com.android.builder.merge.StreamMergeAlgorithms.lambda$select$3(StreamMergeAlgorithms.java:95)
    at com.android.builder.merge.IncrementalFileMergerOutputs$1.create(IncrementalFileMergerOutputs.java:88)
    ... 37 more

Getting started

Hello,

This isn't really an issue as much as it is a request in getting help with using BLE made easy. I'm some what new to app development and new to Kotlin.

Background
I'm currently working on a project where I want to control LED lights via ble. My first step is to do a simple toggle the lights on and off. I'm using HM-10 BLE module to receive signals and an arduino uno to control the signals to the lights. I've used the dabble app to test my HM-10 and arduino uno code and have successfully seen that the lights turn on. Now I would like to build my own app to control the lights from my phone.

What I'm Seeing
I've imported ble-made-easy into android studio and made sure there are no errors. When I test the app (via wireless debugging) on my device I only see main activity (screen title says "AndroidEasyBLE"). No list view or text views or buttons.

Do I need to do anything in the main activity to inflate the fragments and bind correctly? Is there an example I can run out of the box, where I can scan, and connect to a BLE device?

Thank you very much!
Swarandeep Singh

Can not read from Characteristic

Hi,
I find very interesting your library but am facing some issue.
What i want to do:

  • scan ble devices with special service
  • when the devices is discovered, connect to it, and read from a special Characteristic, its fullname

I want to read the fullname cause during the scanning process, i got only 8 bytes of the name.

But when i passe the characteristic to it, instead of reading the fullname of the devices, it sends "null"

// call in fragment
fun startScanning(bl: BLE, scope: CoroutineScope) {
        val scanFilterList = mutableListOf<ScanFilter>()
        scanFilterList.add(ScanFilter.Builder().setServiceUuid(ParcelUuid(SERVICE_UUID), parcelUuidMask).build())

        val scanSettings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE).build()
        scope.launch {
            bl.scanAsync(
                filters = scanFilterList,
                settings = scanSettings,
                duration = 20000,
                onDiscover = { device ->
                    Logger.warn("BT: $device")
                    connectId(bl, device)
                },
                onFinish = { devices ->
                    Logger.warn("BTS: $devices")
                },
                onError = { errorCode ->
                    Logger.warn("ERROR: $errorCode")
                }
            )
        }
    }
private fun connectId(bl: BLE, device: BLEDevice) {
        presenterScope.launch {
            bl.connect(device)?.let { connection ->
                if (connection.isActive) {
                    //connection.write(AllGattCharacteristics.DEVICE_NAME, byteArrayOf(0))
                    val bytes = connection.read(AllGattCharacteristics.DEVICE_NAME)
                    Logger.warn("BT_: ${bytes?.let { String(it) }}") // it sends null instead of the device name
                }
                connection.close()
            }
        }
    }

do you think, what am trying to archive can work?

Thx

Stackoverflow exception while scanning

Hey I am using this from the docs:

GlobalScope.launch {
    try {
        // Continue with your code handling all the devices found
        val devices = ble.scan(duration = 10000)
    } catch (e: Exception) {
        // Show an Alert or UI with your preferred error message
    } catch (e: ScanFailureException) {
        // Show an Alert or UI with your preferred error message
    }
}

I have put it in my ViewModel (using viewModelScope), but have tested with both scopes. When I remove the line "val devices = ble.scan(duration = 10000)" I don't get the exception, does anyone know why I'm getting this?

FATAL EXCEPTION: main
PID: 27338
java.lang.StackOverflowError: stack size 8188KB

Failed to read characteristic

Hi there,

I'm pretty new with kotlin and bluetooth and I'm trying your nice library, scan device works perfectly, but when I want to connect to a device and read a characteristic I get a null return and this is what I get in log : (failed to read characteristic)

image

Here is my code :

 * Connect BLE device
 */
@SuppressLint("MissingPermission")
public fun connectBLE(ble:BLE, device: BLEDevice) {
    var name: String = device.name.toString()
    lifecycleScope.launch {
            ble.connect(device)?.let { connection ->
                // For watching bytes
                val value = connection.read("00002A29-0000-1000-8000-00805F9B34FB")
                Log.i("BLEDATA", "$value")

        }
}

I read that for reading 16 bits uuid with bluetooth low energy we have make uuid this form :
0000XXXX-0000-1000-8000โ€“00805f9b34fb
But maybe I'm wrong ?

I got the uuid 0x180A with nrf Connect App and this is marked Read and correspond to the manufacturer brand name string.
I bet I'm doing something wrong but dont know what...

Thanks a lot !

Doc?

seems like the doc link is dead?Is there an example app on how to use?

[Question] How to scan for nearby devices?

I modified the startScan function of BluetoothCentralManager.kt
Because the startScan function uses filter, but the filter is empty

private fun startScan(filters: List<ScanFilter>, scanSettings: ScanSettings, scanCallback: ScanCallback) {
    if (bleNotReady()) return
    if (bluetoothScanner == null) {
        bluetoothScanner = bluetoothAdapter.bluetoothLeScanner
    }
    if (bluetoothScanner != null) {
        setScanTimer()
        currentCallback = scanCallback
        currentFilters = filters
       + if (filters.isEmpty()){
       +   bluetoothScanner?.startScan(scanCallback)
       + }else{
            bluetoothScanner?.startScan(filters, scanSettings, scanCallback)
       + }

        Logger.i(TAG, "scan started")
    } else {
        Logger.e(TAG, "starting scan failed")
    }
}

Not able to scan And print

@LeandroSQ after integration of your library i am not able to scan and get BLE devices. I checked increase time out as well

i am creating demo for print 58mm BLE printer i find name and mac address from other app but i dont what i need to pass as "characteristic" for write to printer?

i create receipt format but dont know hot to send this to BLE printer and after compete print disconnect with printer

can you please help me for that

Thanks.

BLE instance in service

I wanted to use the library in Foreground Service, but at the moment it's not possible. Could you please add a context-only constructor?

Using this library with Bottom Navigation

Hello,

I've currently worked through you example and have been able to incorporate the scan and connect functionality into my app. I am currently using a bottom Navigation app structure, where one of my bottom menu items takes the user to a fragment that allows them to set up the connection and separate bottom menu items are different fragments where data can be sent. I've tried to setup the connection variable of type BluetoothConnection in my shared viewModel. However, whenever I navigate to a new fragment to send data, the connection variable pulled from the shared view model is null.

Is there an easy way to set this up using a shared view model?

Thanks!
Swarandeep Singh

MTU size request

Hi there it's me again,

I'm trying to receive data from a notify characteristic, it works perfectly but I only receive 20 bytes when I'm supposed to get 50.

After some research I found that it may be the MTU size :

https://stackoverflow.com/questions/69905359/reading-more-than-20-bytes-in-android-ble
https://punchthrough.com/maximizing-ble-throughput-part-2-use-larger-att-mtu-2/
https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#requestMtu(int)

It may be my device but I would be sure it's not other thing, so I would like to call requestMtu() but don't have any idea how to do that with your library...

If you have any idea :)

Thanks again!

[Bug] Default scan settings are not satisfying legacy devices (< SDK 29)

Hello, thank you for creating this awesome library!

I am currently running into some issues on older devices. I tried running the sample project with 3 different Android versions: Android 6, Android 9 and Android 10. So far only the sample app installed on the Android 10 device managed to output scan results. I have also tested the Android 6 and Android 9 devices with other BLE apps to confirm that the BLE is not faulty. Has the library been tested with pre-Android10 devices before?

How can I wait until BLE is paired.

When I pair my device, it takes a certain amount of time for the connection to be established. The BLE connection is established before bonding(SecurityAuth). Thus, all functions such as "observe" or "read" are no longer functional. The only way I've found at the moment is to use a long "delay" before the first observe or read.

Default connection priority and implementing requestConnectionPriority support

Hello,

I am using your library for Bluetooth communication in my project, and I am experiencing some frame loss during data transmission. I am curious about the default connection priority used by your library and if it's possible to implement the requestConnectionPriority function to provide better control over the connection quality.

Could you please let me know the default connection priority value used in your library? Also, would it be possible to add support for the requestConnectionPriority function, so that we can adjust the connection priority based on our requirements (e.g., BluetoothGatt.CONNECTION_PRIORITY_HIGH, BluetoothGatt.CONNECTION_PRIORITY_BALANCED, or BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER)? This feature would be really helpful in improving the data transmission reliability and reducing frame losses.

Thank you for your time and consideration. I appreciate your work on this library, and I am looking forward to your response.

Best regards,

Build Failed ':app:mergeDebugJavaResource'

When I import this library I get a build error.

build.gradle(Project: EKIsSmartWatch)

buildscript {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '7.4.1' apply false
    id 'com.android.library' version '7.4.1' apply false
    id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
}

build.gradle(Module :app)


plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.ekissmartwatch'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.ekissmartwatch"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}


dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'com.github.LeandroSQ:android-ble-made-easy:1.8.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

settings.gradle(Project Settings)

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}
rootProject.name = "EKIsSmartWatch"
include ':app'
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:mergeDebugJavaResource'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
   > 2 files found with path 'META-INF/NOTICE.md' from inputs:
      - C:\Users\reder\.gradle\caches\modules-2\files-2.1\jakarta.xml.bind\jakarta.xml.bind-api\2.3.2\8d49996a4338670764d7ca4b85a1c4ccf7fe665d\jakarta.xml.bind-api-2.3.2.jar
      - C:\Users\reder\.gradle\caches\modules-2\files-2.1\jakarta.activation\jakarta.activation-api\1.2.1\562a587face36ec7eff2db7f2fc95425c6602bc1\jakarta.activation-api-1.2.1.jar
     Adding a packagingOptions block may help, please refer to
     https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
     for more information

* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugJavaResource'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:142)
	at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:140)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:128)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:69)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:327)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:314)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:307)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:293)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:417)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:339)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
	at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:339)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.lambda$waitForItemsAndGatherFailures$2(DefaultAsyncWorkTracker.java:130)
	at org.gradle.internal.Factories$1.create(Factories.java:31)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:321)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:304)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLock(DefaultWorkerLeaseService.java:309)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:126)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:92)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:78)
	at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:66)
	at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:244)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:221)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:204)
	at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:187)
	at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:165)
	at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
	at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
	at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:50)
	at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:40)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
	at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
	at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
	at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
	at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
	at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.executeDelegateBroadcastingChanges(CaptureStateAfterExecutionStep.java:124)
	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:80)
	at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:58)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48)
	at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
	at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:181)
	at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:71)
	at org.gradle.internal.Either$Right.fold(Either.java:175)
	at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:59)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:69)
	at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:47)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:36)
	at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:25)
	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
	at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:110)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:56)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:56)
	at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:73)
	at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:44)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:89)
	at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:50)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:114)
	at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:57)
	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:76)
	at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:50)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.executeWithNoEmptySources(SkipEmptyWorkStep.java:254)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:91)
	at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:56)
	at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:32)
	at org.gradle.internal.execution.steps.RemoveUntrackedExecutionStateStep.execute(RemoveUntrackedExecutionStateStep.java:21)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:43)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:31)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
	at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:281)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
	at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:33)
	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:139)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:128)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:69)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:327)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:314)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:307)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:293)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:417)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:339)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
Caused by: com.android.builder.merge.DuplicateRelativeFileException: 2 files found with path 'META-INF/NOTICE.md' from inputs:
 - C:\Users\reder\.gradle\caches\modules-2\files-2.1\jakarta.xml.bind\jakarta.xml.bind-api\2.3.2\8d49996a4338670764d7ca4b85a1c4ccf7fe665d\jakarta.xml.bind-api-2.3.2.jar
 - C:\Users\reder\.gradle\caches\modules-2\files-2.1\jakarta.activation\jakarta.activation-api\1.2.1\562a587face36ec7eff2db7f2fc95425c6602bc1\jakarta.activation-api-1.2.1.jar
Adding a packagingOptions block may help, please refer to
https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
for more information
	at com.android.builder.merge.IncrementalFileMergerOutputs$1.create(IncrementalFileMergerOutputs.java:93)
	at com.android.builder.merge.DelegateIncrementalFileMergerOutput.create(DelegateIncrementalFileMergerOutput.java:65)
	at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate$run$output$1.create(MergeJavaResourcesDelegate.kt:179)
	at com.android.builder.merge.IncrementalFileMerger.updateChangedFile(IncrementalFileMerger.java:252)
	at com.android.builder.merge.IncrementalFileMerger.mergeChangedInputs(IncrementalFileMerger.java:213)
	at com.android.builder.merge.IncrementalFileMerger.merge(IncrementalFileMerger.java:90)
	at com.android.build.gradle.internal.tasks.MergeJavaResourcesDelegate.run(MergeJavaResourcesDelegate.kt:225)
	at com.android.build.gradle.internal.tasks.MergeJavaResWorkAction.run(MergeJavaResWorkAction.kt:88)
	at com.android.build.gradle.internal.profile.ProfileAwareWorkAction.execute(ProfileAwareWorkAction.kt:74)
	at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
	at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
	at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:199)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:73)
	at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
	at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
	at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$2(DefaultWorkerExecutor.java:205)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162)
	at org.gradle.internal.Factories$1.create(Factories.java:31)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:249)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:109)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:114)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157)
	at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126)
	... 2 more
Caused by: com.android.builder.merge.DuplicateRelativeFileException: 2 files found with path 'META-INF/NOTICE.md'.
Adding a packagingOptions block may help, please refer to
https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
for more information
	at com.android.builder.merge.StreamMergeAlgorithms.lambda$acceptOnlyOne$2(StreamMergeAlgorithms.java:75)
	at com.android.builder.merge.StreamMergeAlgorithms.lambda$select$3(StreamMergeAlgorithms.java:95)
	at com.android.builder.merge.IncrementalFileMergerOutputs$1.create(IncrementalFileMergerOutputs.java:88)
	... 37 more


* Get more help at https://help.gradle.org

BUILD FAILED in 3s
31 actionable tasks: 13 executed, 18 up-to-date

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.