Filter bonded device in bluetooth android - android

I am building a Health application in which blood pressure monitor is used. I already paired so many BLE devices. I want to filter only particular device like Blood Pressure Monitor. I also did this in scan filter through like this
BloodPressurePairViewModel.kt
class BloodPressurePairViewModel : ViewModel() {
companion object {
private const val SCAN_PERIOD: Long = 10000 // For 1 minute scan
private const val UUID_MASK_STRING = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"
private const val BLUETOOTH_PRESSURE_128_STRING = "00001810-0000-1000-8000-00805f9b34fb"
private const val BLUETOOTH_PRESSURE_MEASUREMENT_128_STRING = "00002A35-0000-1000-8000-00805f9b34fb"
}
var bluetoothLeScanner: BluetoothLeScanner? = null
var isBluetoothEnabled by mutableStateOf(false)
private set
val mLeDevices: MutableStateFlow<List<ScanResult>> = MutableStateFlow(emptyList())
var scanning: MutableStateFlow<Boolean> = MutableStateFlow(true)
private set
private val leScanCallback: ScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
if (!mLeDevices.value.equals(result)) {
if (result.device?.name != null) {
if (checkDuplicateScanResult(mLeDevices.value, result)) {
mLeDevices.value += result
}
}
}
}
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
super.onBatchScanResults(results)
logE("onBatchScanResults >> $results")
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
logE("onScanFailed >> $errorCode")
}
}
fun setBluetoothEnable(newValue: Boolean) {
isBluetoothEnabled = newValue
}
fun startScan() {
viewModelScope.launch {
delay(SCAN_PERIOD)
scanning.value = false
bluetoothLeScanner?.startScan(scanFilters(), ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_POWER).build(), leScanCallback)
}
}
private fun scanFilters(): MutableList<ScanFilter> {
val scanFilterList = mutableListOf<ScanFilter>()
val parcelUuidMask = ParcelUuid.fromString(UUID_MASK_STRING)
val listItem = listOf(BLUETOOTH_PRESSURE_128_STRING, BLUETOOTH_PRESSURE_MEASUREMENT_128_STRING)
listItem.forEach {
val scanFilter = ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(it), parcelUuidMask).build()
scanFilterList.add(scanFilter)
}
return scanFilterList
}
fun stopScan() {
bluetoothLeScanner?.stopScan(leScanCallback)
}
private fun checkDuplicateScanResult(value: List<ScanResult>, result: ScanResult): Boolean {
val checkDevice = value.count { it.device == result.device }
return checkDevice < 1
}
}
I used scanFilters() to find particular type of device like BPM. Now I want to filter according to my 128 bit string in my bonded device. I tried this piece of code from this stack overflow but it didn't work. I tried this piece of code
val getUuidsMethod: Method = BluetoothAdapter::class.java.getDeclaredMethod("getUuids", null)
val uuids = getUuidsMethod.invoke(bluetoothAdapter, null)
but it giving me error
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:526)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) 
Caused by: java.lang.NoSuchMethodException: parameter type is null
at java.lang.Class.getMethod(Class.java:2063)
at java.lang.Class.getDeclaredMethod(Class.java:2050)
at com.abc.app.bloodpressure.composables.PairScreenState.getPairedDevice(BloodPressurePairScreen.kt:176)
at com.abc.app.bloodpressure.composables.PairScreenState.<init>(BloodPressurePairScreen.kt:156)
at com.abc.app.bloodpressure.composables.BloodPressurePairScreenKt.rememberPairScreenState(BloodPressurePairScreen.kt:118)
at com.abc.app.bloodpressure.composables.BloodPressurePairScreenKt.BluetoothPairContentStateful(BloodPressurePairScreen.kt:66)
at com.abc.app.bloodpressure.composables.BloodPressurePairScreenKt.BluetoothPairContent(BloodPressurePairScreen.kt:52)
at com.abc.app.bloodpressure.composables.BluetoothRequestScreenKt$BluetoothRequestContent$1.invoke(BluetoothRequestScreen.kt:39)
at com.abc.app.bloodpressure.composables.BluetoothRequestScreenKt$BluetoothRequestContent$1.invoke(BluetoothRequestScreen.kt:35)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
Can you guys help me on this?

Related

Error getting native address of native library: task_vision_jni_gms

I am trying to integrate a tensorflow-lite model in my android app. I have a simple model to differentiate between cats and dogs.I downloaded the required dataset from kaggle and used teachable machine website to train the model. Then I downloaded the model as tensorflow-lite and selected the quantized option. Below is my android code to detect the model.
class ObjectDetectorHelper(
var threshold: Float = 0.5f,
var numThreads: Int = 2,
var maxResults: Int = 1,
var currentDelegate: Int = 0,
var currentModel: Int = 0,
val context: Context,
val objectDetectorListener: DetectorListener
) {
private val TAG = "ObjectDetectionHelper"
// For this example this needs to be a var so it can be reset on changes. If the ObjectDetector
// will not change, a lazy val would be preferable.
private var objectDetector: ObjectDetector? = null
private var gpuSupported = false
init {
TfLiteGpu.isGpuDelegateAvailable(context).onSuccessTask { gpuAvailable: Boolean ->
val optionsBuilder =
TfLiteInitializationOptions.builder()
if (gpuAvailable) {
optionsBuilder.setEnableGpuDelegateSupport(true)
}
TfLiteVision.initialize(context, optionsBuilder.build())
}.addOnSuccessListener {
objectDetectorListener.onInitialized()
}.addOnFailureListener{
objectDetectorListener.onError("TfLiteVision failed to initialize: "
+ it.message)
}
}
fun clearObjectDetector() {
objectDetector = null
}
// Initialize the object detector using current settings on the
// thread that is using it. CPU and NNAPI delegates can be used with detectors
// that are created on the main thread and used on a background thread, but
// the GPU delegate needs to be used on the thread that initialized the detector
private fun setupObjectDetector() {
if (!TfLiteVision.isInitialized()) {
Log.e(TAG, "setupObjectDetector: TfLiteVision is not initialized yet")
return
}
// Create the base options for the detector using specifies max results and score threshold
val optionsBuilder =
ObjectDetector.ObjectDetectorOptions.builder()
.setScoreThreshold(threshold)
.setMaxResults(maxResults)
// Set general detection options, including number of used threads
val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)
// Use the specified hardware for running the model. Default to CPU
when (currentDelegate) {
DELEGATE_CPU -> {
// Default
}
DELEGATE_GPU -> {
if (gpuSupported) {
baseOptionsBuilder.useGpu()
} else {
objectDetectorListener.onError("GPU is not supported on this device")
}
}
DELEGATE_NNAPI -> {
baseOptionsBuilder.useNnapi()
}
}
optionsBuilder.setBaseOptions(baseOptionsBuilder.build())
val modelName =
when (currentModel) {
MODEL_MOBILENETV1 -> "model.tflite"
MODEL_EFFICIENTDETV0 -> "model.tflite"
MODEL_EFFICIENTDETV1 -> "model.tflite"
MODEL_EFFICIENTDETV2 -> "model.tflite"
else -> "model.tflite"
}
try {
objectDetector =
ObjectDetector.createFromFileAndOptions(context, modelName, optionsBuilder.build())
} catch (e: Exception) {
objectDetectorListener.onError(
"Object detector failed to initialize. See error logs for details"
)
Log.e(TAG, "TFLite failed to load model with error: " + e.message)
}
}
fun detect(image: Bitmap, imageRotation: Int) {
Log.i("resultssss","9")
if (!TfLiteVision.isInitialized()) {
Log.e(TAG, "detect: TfLiteVision is not initialized yet")
return
}
Log.i("resultssss","10")
if (objectDetector == null) {
setupObjectDetector()
}
Log.i("resultssss","11")
// Inference time is the difference between the system time at the start and finish of the
// process
var inferenceTime = SystemClock.uptimeMillis()
Log.i("resultssss","12")
// Create preprocessor for the image.
// See https://www.tensorflow.org/lite/inference_with_metadata/
// lite_support#imageprocessor_architecture
val imageProcessor = ImageProcessor.Builder().add(Rot90Op(-imageRotation / 90)).build()
Log.i("resultssss","13")
// Preprocess the image and convert it into a TensorImage for detection.
val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))
Log.i("resultssss","14")
val results = objectDetector?.detect(tensorImage)
Log.i("resultssss","15")
inferenceTime = SystemClock.uptimeMillis() - inferenceTime
Log.i("resultssss","16")
objectDetectorListener.onResults(
results,
inferenceTime,
tensorImage.height,
tensorImage.width)
}
interface DetectorListener {
fun onInitialized()
fun onError(error: String)
fun onResults(
results: MutableList<Detection>?,
inferenceTime: Long,
imageHeight: Int,
imageWidth: Int
)
}
companion object {
const val DELEGATE_CPU = 0
const val DELEGATE_GPU = 1
const val DELEGATE_NNAPI = 2
const val MODEL_MOBILENETV1 = 0
const val MODEL_EFFICIENTDETV0 = 1
const val MODEL_EFFICIENTDETV1 = 2
const val MODEL_EFFICIENTDETV2 = 3
}
}
class MainActivity : AppCompatActivity(), ObjectDetectorHelper.DetectorListener {
private lateinit var cameraExecutor: ExecutorService
private var mCameraProvider: ProcessCameraProvider? = null
private lateinit var viewFinder: PreviewView
private lateinit var objectDetectorHelper: ObjectDetectorHelper
private lateinit var bitmapBuffer: Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewFinder = findViewById(R.id.viewFinder)
objectDetectorHelper = ObjectDetectorHelper(
context = this,
objectDetectorListener = this
)
}
private fun setUpCamera() {
if (allPermissionsGranted()) {
startCamera()
}
}
private fun detectObjects(image: ImageProxy) {
Log.i("resultssss", "5")
// Copy out RGB bits to the shared bitmap buffer
image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }
Log.i("resultssss", "6")
val imageRotation = image.imageInfo.rotationDegrees
Log.i("resultssss", "7")
// Pass Bitmap and rotation to the object detector helper for processing and detection
objectDetectorHelper.detect(bitmapBuffer, imageRotation)
Log.i("resultssss", "8")
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
mCameraProvider = cameraProvider
// Preview
val surfacePreview = Preview.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setTargetRotation(viewFinder.display.rotation)
.build()
.also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
val imageAnalyzer =
ImageAnalysis.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setTargetRotation(viewFinder.display.rotation)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build()
// The analyzer can then be assigned to the instance
.also {
Log.i("resultssss", "1")
it.setAnalyzer(cameraExecutor) { image ->
Log.i("resultssss", "2")
if (!::bitmapBuffer.isInitialized) {
Log.i("resultssss", "3")
// The image rotation and RGB image buffer are initialized only once
// the analyzer has started running
bitmapBuffer = Bitmap.createBitmap(
image.width,
image.height,
Bitmap.Config.ARGB_8888
)
}
Log.i("resultssss", "4")
detectObjects(image)
}
}
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, surfacePreview, imageAnalyzer
)
} catch (exc: Exception) {
Toast.makeText(this, exc.message, Toast.LENGTH_LONG).show()
}
}, ContextCompat.getMainExecutor(this))
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
baseContext, it
) == PackageManager.PERMISSION_GRANTED
}
override fun onDestroy() {
super.onDestroy()
objectDetectorHelper.clearObjectDetector()
cameraExecutor.shutdown()
}
companion object {
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS =
mutableListOf(
android.Manifest.permission.CAMERA
).toTypedArray()
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
setUpCamera()
} else {
Toast.makeText(
this,
"Permissions not granted by the user.",
Toast.LENGTH_SHORT
).show()
finish()
}
}
}
override fun onInitialized() {
if (allPermissionsGranted()) {
setUpCamera()
} else {
ActivityCompat.requestPermissions(
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
)
}
cameraExecutor = Executors.newSingleThreadExecutor()
}
override fun onError(error: String) {
runOnUiThread { Toast.makeText(this, error, Toast.LENGTH_SHORT).show() }
}
override fun onResults(
results: MutableList<Detection>?,
inferenceTime: Long,
imageHeight: Int,
imageWidth: Int
) {
runOnUiThread {
Log.i(
"resultssss",
"${results?.get(0)?.categories.toString()} ${results?.get(0)?.boundingBox.toString()}"
)
}
}
}
Complete error log is as follows
Error getting native address of native library: task_vision_jni_gms
java.lang.IllegalArgumentException: Error occurred when initializing ObjectDetector: Mobile SSD models are expected to have exactly 4 outputs, found 1
at org.tensorflow.lite.task.gms.vision.detector.ObjectDetector.initJniWithModelFdAndOptions(Native Method)
at org.tensorflow.lite.task.gms.vision.detector.ObjectDetector.zzb(Unknown Source:0)
at org.tensorflow.lite.task.gms.vision.detector.zzb.createHandle(org.tensorflow:tensorflow-lite-task-vision-play-services##0.4.2:4)
at org.tensorflow.lite.task.core.TaskJniUtils$1.createHandle(TaskJniUtils.java:70)
at org.tensorflow.lite.task.core.TaskJniUtils.createHandleFromLibrary(TaskJniUtils.java:91)
at org.tensorflow.lite.task.core.TaskJniUtils.createHandleFromFdAndOptions(TaskJniUtils.java:66)
at org.tensorflow.lite.task.gms.vision.detector.ObjectDetector.createFromFileAndOptions(org.tensorflow:tensorflow-lite-task-vision-play-services##0.4.2:2)
at com.affinidi.tfdemoone.ObjectDetectorHelper.setupObjectDetector(ObjectDetectorHelper.kt:104)
at com.affinidi.tfdemoone.ObjectDetectorHelper.detect(ObjectDetectorHelper.kt:121)
at com.affinidi.tfdemoone.MainActivity.detectObjects(MainActivity.kt:89)
at com.affinidi.tfdemoone.MainActivity.startCamera$lambda$4$lambda$3$lambda$2(MainActivity.kt:132)
at com.affinidi.tfdemoone.MainActivity.$r8$lambda$cwS3iJ069sufgGf-nT7H81EEGtQ(Unknown Source:0)
at com.affinidi.tfdemoone.MainActivity$$ExternalSyntheticLambda3.analyze(Unknown Source:2)
at androidx.camera.core.ImageAnalysis.lambda$setAnalyzer$2(ImageAnalysis.java:481)
at androidx.camera.core.ImageAnalysis$$ExternalSyntheticLambda2.analyze(Unknown Source:2)
at androidx.camera.core.ImageAnalysisAbstractAnalyzer.lambda$analyzeImage$0$androidx-camera-core-ImageAnalysisAbstractAnalyzer(ImageAnalysisAbstractAnalyzer.java:286)
at androidx.camera.core.ImageAnalysisAbstractAnalyzer$$ExternalSyntheticLambda1.run(Unknown Source:14)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
Checking model.tflite revealed that the model you trained is a classification model but you are using the ObjectDetector APIs.
Debugging,
# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(TFLITE_FILE_PATH)
interpreter.allocate_tensors()
# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
# Test the model on cat image
from PIL import Image
image = Image.open("/home/vijay/Downloads/cat.jpg")
input_shape = input_details[0]['shape']
interpreter.set_tensor(input_details[0]['index'], img[None,...])
interpreter.invoke()
#get output
output_data = interpreter.get_tensor(output_details[0]['index'])
#output_data [255, 0]--> idx 0 ---> cat
#checking the above for a dog photo
#output_data [0, 255]--> idx 1 ---> dog
As the error points out, you will get only one output in a classification model. So check the android example on how to handle classification problem: https://github.com/tensorflow/examples/tree/master/lite/examples/image_classification/android

FlexboxLayoutManager throw an indexOutOfBoundException

We have a RecyclerView with a FlexboxLayoutManager witch throw an IndexOutOfBound on each scroll of the recyclerView.
FlexBox version: 2.0.1
Kotlin version: 1.4.30
Our code
C = EbookFlexController(this)
B.recycler.setItemViewCacheSize(10)
C.adapter.hasStableIds()
B.recycler.adapter = C.adapter
B.recycler.layoutManager = getFlexLayoutManager()
C represents the FlexBoxController
class FlexController(private val callbacks: Callbacks) : TypedEpoxyController<StreamCluster?>() {
interface Callbacks {
fun onAppClick(app: AppItem)
fun onAppLongClick(app: AppItem)
}
override fun buildModels(streamCluster: StreamCluster?) {
if (streamCluster == null) {
for (i in 1..8) {
add(
AppViewShimmerModel_()
.id(i)
)
}
} else {
streamCluster.appList.forEach { app ->
add(
AppViewModel_()
.id(app.packageName)
.click { _ ->
callbacks.onAppClick(app)
}
.longClick { _ ->
callbacks.onAppLongClick(app)
false
}
.app(app)
)
}
}
}
}
The stream Cluster
data class EbookStreamCluster(val id: String = UUID.randomUUID().toString()) {
var title: String = String()
var subtitle: String = String()
var category: String = String()
var url: String = String()
var ebookList: MutableList<EbookItem> = ArrayList()
var hasNext: Boolean = false
var isPaid:Boolean = false
var page: Int = 0
var type: Type = Type.CLUSTER
}
On firebase, we have this error:
Process: com.gara.store, PID: 3655
java.lang.IndexOutOfBoundsException: Index: 18, Size: 18
at java.util.ArrayList.get(ArrayList.java:437)
at com.google.android.flexbox.FlexboxLayoutManager.recycleFlexLinesFromStart(FlexboxLayoutManager.java:1335)
at com.google.android.flexbox.FlexboxLayoutManager.recycleByLayoutState(FlexboxLayoutManager.java:1315)
at com.google.android.flexbox.FlexboxLayoutManager.fill(FlexboxLayoutManager.java:1302)
at com.google.android.flexbox.FlexboxLayoutManager.handleScrollingMainOrientation(FlexboxLayoutManager.java:1974)
at com.google.android.flexbox.FlexboxLayoutManager.scrollVerticallyBy(FlexboxLayoutManager.java:1935)
at androidx.recyclerview.widget.RecyclerView.scrollStep(RecyclerView.java:1972)
at androidx.recyclerview.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:5476)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1037)
at android.view.Choreographer.doCallbacks(Choreographer.java:845)
at android.view.Choreographer.doFrame(Choreographer.java:775)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7870)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
The same lib is used somewhere in the app and works without bugs. We don't know why with the exact same code, it is not working.
Please how could we do ?
Thanks

The On Success Listener for query.get() won't execute properly. The code returns an error saying that the load has not been initialized

So, my roommate and I are trying to develop an app to help students living on campus at our school keep track of their laundry. However, we are having trouble creating new laundry loads.
Our addLoad function is supposed to add a LaundryHolder object to Firebase (containing the machine number, whether it is a washer or dryer, who owns the load, and how many seconds are left for the load), whereas the LaundryLoad object contains a LaundryHolder, observer function (notifyDataSetChanged() for the LaundryLoadFragment), and timer (with time form LaundryHolder).
In Firebase, each clothingItem has a load ID with which to identify which load it is in on the user side. For our implementation to work, we need to fetch the ID which Firebase gives our LaundryHolder, which is why we are adding an onSuccessListener to a temporary query. The issue arises, however, when the query doesn't succeed or fail, and we can't figure out what is going on here.
This is the error we get:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: edu.rosehulman.roselaundrytracker, PID: 11847
kotlin.UninitializedPropertyAccessException: lateinit property load has not been initialized
at edu.rosehulman.roselaundrytracker.model.LaundryLoadViewModel.addLoad(LaundryLoadViewModel.kt:42)
at edu.rosehulman.roselaundrytracker.adapter.AddLoadAdapter.addLoad(AddLoadAdapter.kt:67)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment.onCreateView$lambda-1(AddLoadFragment.kt:32)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment.$r8$lambda$lIyFvxsLH_bCt-kHzadMjy2Ls_Y(Unknown Source:0)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7455)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7432)
at android.view.View.access$3700(View.java:835)
at android.view.View$PerformClick.run(View.java:28810)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Does anyone have any idea?
class LaundryLoadViewModel: ViewModel() {
private var loads = ArrayList<LaundryLoad>()
private var curPos = 0
lateinit var ref: CollectionReference
lateinit var uid: String
private var onlyOwned = true
private val subscriptions = HashMap<String, ListenerRegistration>()
fun getPreference() = onlyOwned
fun addLoad(machineNumber: Int, machineType: String, contents: ArrayList<ClothingItem>, time: Long, observer: () -> Unit){
val holder = LaundryHolder(machineNumber, machineType.lowercase(Locale.getDefault()) == "dryer", time * LaundryLoadFragment.SEC_TO_MIN, uid)
// val load = LaundryLoad(holder, observer)
// loads.add(load)
ref.add(holder)
lateinit var load: LaundryLoad
val query = ref
.whereEqualTo("machineNumber",machineNumber)
.whereEqualTo("owner",uid)
query.get().addOnSuccessListener { snapshot ->
snapshot.documents.forEach {
Log.d(Constants.TAG,"Retrieving load from Firebase")
load = LaundryLoad.from(it, observer)
}
}
query.get().addOnFailureListener {
Log.d(Constants.TAG,"Retrieval failed due to $it")
}
// val query = ref.whereEqualTo("machineNumber",machineNumber).whereEqualTo("dryer",machineType.lowercase(Locale.getDefault())=="dryer")
load.addMany(contents)
loads.add(load)
}
fun addListener(fragmentName: String, observer: () -> Unit) {
lateinit var subscription: ListenerRegistration
loads.clear()
val auth = Firebase.auth
val user = auth.currentUser!!
val clothes = ArrayList<ClothingItem>()
uid = user.uid
ref = Firebase.firestore.collection(LaundryLoad.COLLECTION_PATH)
val ref2 = Firebase.firestore.collection(ClothingItem.COLLECTION_PATH)
val inLoadQuery = ref2.whereNotEqualTo("load","")
inLoadQuery.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
snapshot?.documents?.forEach {
clothes.add(ClothingItem.from(it))
}
}
if(onlyOwned) {
val query = ref.whereEqualTo("owner",uid)
subscription = query
.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
retrieveLoads(snapshot, clothes, observer)
}
} else {
subscription = ref
.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
retrieveLoads(snapshot, clothes, observer)
}
}
subscriptions[fragmentName] = subscription
observer()
}
private fun retrieveLoads(snapshot: QuerySnapshot?, clothes: ArrayList<ClothingItem>, observer: () -> Unit) {
snapshot?.documents?.forEach {
loads.add(LaundryLoad.from(it, observer))
}
for (load in loads) {
for (item in clothes) {
if (item.load == load.getId()) {
load.addToLoad(item)
}
}
}
}
fun removeListener(fragmentName: String) {
for(load in loads) {
ref.document(load.getId()).set(load.laundryHolder)
}
subscriptions[fragmentName]?.remove()
subscriptions.remove(fragmentName)
}
fun togglePreference() {
onlyOwned = !onlyOwned
}
}
It looks like ref has not been initialized when you ref.add(holder) in addLoad. It's impossible for us to say why that is, as the code that calls addLoad seems to be missing, but the stack trace should point you pretty directly to where the problem is.

Android BLE RxKotlin

I'm trying to create a BLE service that will scan for devices and using rxKotlin create an observable that will allow another class to observe when a device is found. I'm confused on how to create the observable that will allow another class to subscribe and tutorials are all over the place. Can someone give me a pointer on how to do so or a good tutorial.
Bluetoothservice class callback where devices are discovered
var foundDeviceObservable: Observable<BluetoothDevice> = Observable.create { }
private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
with(result.device) {
var foundName = if (name == null) "N/A" else name
foundDevice = BluetoothDevice(
foundName,
address,
address,
result.device.type.toString()
)
foundDeviceObservable.subscribe {
//Update Observable value?
}
}
}
}
class DeviceListViewModel(application: Application) : AndroidViewModel(application) {
private val bluetoothService = BLEService()
//Where I am trying to do logic with device
fun getDeviceObservable(){
bluetoothService.getDeviceObservable().subscribe{ it ->
}
}
Solution
Was able to find the solution after reading user4097210's reply. Just had to change the found device to
var foundDeviceObservable: BehaviorSubject<BluetoothDevice> = BehaviorSubject.create()
and then call the next method in the callback
private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
with(result.device) {
var foundName = if (name == null) "N/A" else name
foundDevice = BluetoothDevice(
foundName,
address,
address,
result.device.type.toString()
)
foundDeviceObservable.onNext(foundDevice)
}
}
}
use BehaviorSubject
// create a BehaviorSubject
var foundDeviceObservable: BehaviorSubject<BluetoothDevice> = BehaviorSubject()
// call onNext() to send new found device
foundDeviceObservable.onNext(foundDevice)
// do your logic use foundDeviceObservable
foundDeviceObservable.subscribe(...)

Androidx datastore test: Ensure that you are only creating a single instance of datastore for this file

Currently, I am writing some test for my proto datastore. The only problem I have here is that I can't call a specific function because then my test fails / crashes. I find this very confusing, because all my other functions seem to work, except resetDatastore
Here is my code:
Repository
private companion object {
private const val SHOP_FILTER_PRODUCT_DATASTORE: String = "shop_filter_product_datastore_test"
private const val SHOP_FILTER_LIST_DATASTORE: String = "shop_filter_list_datastore_test"
private const val SHOP_FILTER_BTN_DATASTORE: String = "shop_filter_btn_datastore_test"
}
private val testNonVolatileProductDataStore = context.createDataStore(
fileName = SHOP_FILTER_PRODUCT_DATASTORE,
serializer = ShopFilterProductSerializer
)
private val testNonVolatileListDataStore = context.createDataStore(
fileName = SHOP_FILTER_LIST_DATASTORE,
serializer = ShopFilterListSerializer
)
private val testNonVolatileBtnDataStore = context.createDataStore(
fileName = SHOP_FILTER_BTN_DATASTORE,
serializer = ShopFilterBtnSerializer
)
override suspend fun setValueProduct(newProduct: ShopFilterTempHolder) {
if (newProduct.id == null || newProduct.mQuery == null) return
testNonVolatileProductDataStore.updateData { preferences ->
preferences.toBuilder().apply {
positionId = newProduct.id!!
query = newProduct.mQuery
}.build()
}
}
override suspend fun setValueList(newList: ShopFilterTempHolder) {
if (newList.id == null || newList.mQuery == null) return
testNonVolatileListDataStore.updateData { preferences ->
preferences.toBuilder().apply {
positionId = newList.id!!
query = newList.mQuery
mQueryDirection = newList.mQueryDirection
}.build()
}
}
override suspend fun setShopFilterBtn(value: Boolean) {
testNonVolatileBtnDataStore.updateData { preferences ->
preferences.toBuilder().apply {
isChecked = value
}.build()
}
}
override suspend fun peekProductValue(): ShopFilterTempHolder {
val temp = shopFilterProduct.first()
return ShopFilterTempHolder(temp.positionId, temp.query)
}
override suspend fun peekListValue(): ShopFilterTempHolder {
val temp = shopFilterList.first()
return ShopFilterTempHolder(temp.positionId, temp.query, temp.mQueryDirection)
}
override suspend fun peekBtnValue(): Boolean = mappedShopFilterBtn.first()
override suspend fun resetDatastore() {
testNonVolatileProductDataStore.updateData { preferences ->
preferences.toBuilder().apply {
positionId = Constants.SHOP_FILTER_DEFAULT_PRODUCT_ID
query = Constants.SHOP_FILTER_DEFAULT_PRODUCT_QUERY
}.build()
}
testNonVolatileListDataStore.updateData { preferences ->
preferences.toBuilder().apply {
positionId = Constants.SHOP_FILTER_DEFAULT_LIST_ID
query = Constants.SHOP_FILTER_DEFAULT_LIST_QUERY
mQueryDirection = Constants.SHOP_FILTER_DEFAULT_LIST_QUERY_DIRECTION
}.build()
}
testNonVolatileBtnDataStore.updateData { preferences ->
preferences.toBuilder().apply {
isChecked = true
}.build()
}
}
Test
#Test
fun `values should be set to default`() = runBlocking {
val newBtn = false
val newList = ShopFilterTempHolder(0, "testString", 0)
val newProduct = ShopFilterTempHolder(0, "testString", 0)
shopFilterValidator.tempBtnFilterValue = newBtn
shopFilterValidator.tempListFilter = newList
shopFilterValidator.tempProductFilter = newProduct
shopFilterValidator.setNewBtnFilter()
shopFilterValidator.setNewListFilter()
shopFilterValidator.setNewProductFilter()
assertEquals(newProduct, shopFilterDataStoreRepository.peekProductValue())
assertEquals(newList, shopFilterDataStoreRepository.peekListValue())
assertEquals(newBtn, shopFilterDataStoreRepository.peekBtnValue())
shopFilterValidator.deleteAllValues()
assertEquals(defautTempProductFilter, shopFilterDataStoreRepository.peekProductValue())
assertEquals(defaultTempListFilter, shopFilterDataStoreRepository.peekListValue())
assertEquals(defaultTempBtnFilterValue, shopFilterDataStoreRepository.peekBtnValue())
}
Stacktrace
Exception in thread "DefaultDispatcher-worker-2 #coroutine#5" java.io.IOException: Unable to rename C:\Users\Censored\AppData\Local\Temp\robolectric-Method_values_should_be_set_to_default1366629743868428403\com.example.app-dataDir\files\datastore\shop_filter_product_datastore_test.tmp.This likely means that there are multiple instances of DataStore for this file. Ensure that you are only creating a single instance of datastore for this file.
at androidx.datastore.core.SingleProcessDataStore.writeData$datastore_core(SingleProcessDataStore.kt:303)
at androidx.datastore.core.SingleProcessDataStore.transformAndWrite(SingleProcessDataStore.kt:280)
at androidx.datastore.core.SingleProcessDataStore$actor$1.invokeSuspend(SingleProcessDataStore.kt:165)
(Coroutine boundary)
at kotlinx.coroutines.CompletableDeferredImpl.await(CompletableDeferred.kt:86)
at androidx.datastore.core.SingleProcessDataStore$updateData$2.invokeSuspend(SingleProcessDataStore.kt:96)
at androidx.datastore.core.SingleProcessDataStore.updateData(SingleProcessDataStore.kt:96)
at com.example.app.repository.FakeDataStoreRepositoryImpl.deleteDataStore(FakeDataStoreRepositoryImpl.kt:86)
at com.example.app.data.models.validator.ShopFilterValidator$deleteAllValues$1.invokeSuspend(ShopFilterValidator.kt:80)
not sure if that could help you, but in my case the problem occurred when running tests on Windows machine and wasn't there when switching to Linux or executing the test on the emulator instead

Categories

Resources