BLE Beacon Scanning with Luch - kotlin

I am playing around with the BLE beacon scanner library Luch in Kotlin.
To scan for beacons I did the following (which is working):
val beaconScanner = BeaconScanner.Builder(context)
.setBeaconParser(beaconParser)
.setBeaconBatchListener { beacons ->
for (beacon in beacons) {
val beaconPower = beacon.txPower
}
}
.setRangingEnabled()
.build()
beaconScanner.start()
However, to calculate the distance for one beacon, one has to use the Ranger class. The code example is:
val ranger = beaconScanner.getRanger()
val distance = ranger.calculateDistance(beacon)
Since the beaconScanner object is a listener, I don't know how to reference the beacon list outside. I tried to save the returned beacons-list and to access it after starting the scanning but this does not seem to work. Inside the setBeaconBatchListener I cannot call the calculateDistance function because it is part of the Ranger class which I don't have access to inside the listener.
Can someone please help me and tell me how this works with listeners?

Related

Push notifications with back stack (Pending Intents, Kotlin)

I'm facing a problem with passing the launch URL from one activity to another, without creating a new Intent for my MainActivity.
I have a webview, which is work with OneSignal push notifications. I wanted to modify the grouping notifications content.
If there's a way to get the result I want (modifying notifications group layout for OneSignal) That would be awesome. I'll simply use the One Signal default action and that would be the best solution for me.
If I have to implement it on that way:
https://developer.android.com/training/notify-user/group
The problem is, when I start a new child activity of the MainActivity, I don't use the "StartActivity / StartActivityForResults" functions.
This is the extension of OSRemoteNotificationReceivedHandler (OneSignal class)
It's outside of my MainActivity class.
class NotificationServiceExtension : OSRemoteNotificationReceivedHandler {
#RequiresApi(Build.VERSION_CODES.N)
override fun remoteNotificationReceived(
context: Context,
notificationReceivedEvent: OSNotificationReceivedEvent
) {
val notification = notificationReceivedEvent.notification
val bigText = Html.fromHtml(notification.body, FROM_HTML_MODE_LEGACY).toString()
var smallText = Html.fromHtml(notification.additionalData["cleantitle"] as String, FROM_HTML_MODE_LEGACY).toString()
val summaryStatistics = Html.fromHtml(notification.additionalData["setSummaryText"] as String, FROM_HTML_MODE_LEGACY).toString()
if (smallText == "test") {
smallText = Html.fromHtml(notification.additionalData["smalltitle"] as String, FROM_HTML_MODE_LEGACY).toString()
}
else{
val name = Html.fromHtml(notification.additionalData["text"] as String, FROM_HTML_MODE_LEGACY).toString()
smallText += " from $name"
}
val smallContent = RemoteViews("com.webviewapp.mywebviewapp", R.layout.small_layout_notification)
val sum = RemoteViews("com.webviewapp.mywebviewapp", R.layout.summary_layout_notification)
val bigContent = RemoteViews("com.webviewapp.mywebviewapp", R.layout.large_notification_layout)
bigContent.setTextViewText(R.id.notification_title, smallText)
bigContent.setTextViewText(R.id.notification_content, bigText)
smallContent.setTextViewText(R.id.notification_title, smallText)
sum.setTextViewText(R.id.notification_title, summaryStatistics)
notificationReceivedEvent.complete(null)
var bp: Bitmap? = null
try {
bp =Picasso.get().load(notification.largeIcon).get()
smallContent.setImageViewBitmap(R.id.noti_pic, bp)
bigContent.setImageViewBitmap(R.id.noti_pic, bp)
}
catch(e:Exception){
print(e)
}
try {
val fid = notification.additionalData["fid"] as String
notificationId = fid.toInt()
}
catch(e:java.lang.Exception){
notificationId += Date().time.toInt()
}
val notificationOpenActivity = Intent(context.applicationContext, MainActivity::class.java)
.putExtra("launchURL", notification.additionalData["pushURL"] as String)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(context.applicationContext).run {
// Add the intent, which inflates the back stack
addNextIntentWithParentStack(notificationOpenActivity)
// Get the PendingIntent containing the entire back stack
getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT)
}
val receivedNotification = NotificationCompat.Builder(context.applicationContext, NOTIFICATION_GROUP)
.setSmallIcon(R.drawable.myIcon)
.setColor(ContextCompat.getColor(context.applicationContext,R.color.blue_primary))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(NotificationCompat.DecoratedCustomViewStyle()) // to expand button
.setAutoCancel(true)
.setVibrate(longArrayOf(500, 500, 500))
.setCustomBigContentView(bigContent)
.setCustomContentView(smallContent)
.setCustomHeadsUpContentView(sum)
.setChannelId(NOTIFICATION_CHANNEL)
.setGroup(NOTIFICATION_GROUP)
.setGroupSummary(false)
.setContentIntent(resultPendingIntent)
.build()
val summary = NotificationCompat.Builder(context.applicationContext, NOTIFICATION_GROUP)
.setSmallIcon(R.drawable.myIcon)
.setColor(ContextCompat.getColor(context.applicationContext,R.color.blue_primary))
//.setContentTitle(summaryStatistics.toString())
.setContentTitle(summaryStatistics)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(NotificationCompat.InboxStyle()
.setBigContentTitle(summaryStatistics)
.addLine(summaryStatistics)
.setSummaryText(summaryStatistics))
.setAutoCancel(true)
.setChannelId(NOTIFICATION_CHANNEL)
.setGroup(NOTIFICATION_GROUP)
.setGroupSummary(true)
.build()
NotificationManagerCompat.from(context.applicationContext).apply {
notify(notificationId, receivedNotification)
notify(SUMMARY_ID, summary)
}
}
}
And the notifications work good as I want. The problem is, How do I pass from the child activity the extra parameter to it's parent if I didn't create that child from the parent? I simply want to load it's URL into my webview, but also keep the back stack.
Also, how can I make sure I don't create multiple MainActivity if I won't use that child?
Thanks in advance.
Notification Back Stack
Android's documentation page "Start an Activity from a Notification" covers the back stack use case under the "Regular activity" suggestion.
Regular activity
This is an activity that exists as a part of your app's normal UX flow. So when the user arrives in the activity from the notification, the new task should include a complete back stack, allowing them to press Back and navigate up the app hierarchy.
I see you are using the addNextIntentWithParentStack method on TaskStackBuilder already in your code so looks like you may have already followed that page.
However there is one thing wrong with Google's docs here, the requestCode sent to getPendingIntent should be a unique value for your app.
Example:
getPendingIntent(
1234, // NOTE: Change this to a unique requestCode for your app
PendingIntent.FLAG_UPDATE_CURRENT
)
I have filed an docs issue with Google on this.
Lastly, since I didn't see this in your question make sure you have correctly added android:parentActivityName to your Activity in your AndroidManifest.xml per Android's "Define your app's Activity hierarchy"
OneSignal Details
Notification Tracking
Note that calling notificationReceivedEvent.complete(null) means OneSignal won't know anything about your notification you're displaying with NotificationManagerCompat. This changes a few things:
Click counts won't be sent to OneSignal
Notification won't be restored. (notifications are automatically cleaned when the app is "force stopped", device is rebooted, or app is updated)
Notification Groups
OneSignal can already do the grouping and summary you have in your code. Just set the "Group Key" on the dashboard, or android_group if you are sending the notification with the REST API.
Back stack
OneSignal doesn't allow you to control the back stack, it simply just always resumes that app and leaves the back stack un-effected. However you can disable this default behavior with com.onesignal.NotificationOpened.DEFAULT in your AndroidManifest.xml and use your own startActivity from the OneSignal.setNotificationOpenedHandler.

How to return an int value stuck in a for loop but a callback in Kotlin?

I am trying to get the size of this firebase collection size of documents, and for some reason in Kotlin, I can't seem to get this to work. I have declared a variable to be zero in an int function and I put it inside a for loop where it increments to the size of the range. Then when I return the value, it is zero. Here is the code I have provided, please help me as to why it is returning zero.
This is just what is being passed to the function
var postSize = 0
That is the global variable, now for below
val db = FirebaseFirestore.getInstance()
val first = db.collection("Post").orderBy("timestamp")
getPostSize(first)
This is the function
private fun getPostSize(first: Query){
first.get().addOnSuccessListener { documents ->
for(document in documents) {
Log.d(TAG, "${document.id} => ${document.data}")
getActualPostSize(postSize++)
}
}
return postSize
}
private fun getActualPostSize(sizeOfPost: Int): Int {
// The number does push to what I am expecting right here if I called a print statement
return sizeOfPost // However here it just returns it to be zero again. Why #tenffour04? Why?
}
It is my understanding, according to the other question that this was linked to, that I was suppose to do something like this.
This question has answers that explain how to approach getting results from asynchronous APIs, like you're trying to do.
Here is a more detailed explanation using your specific example since you were having trouble adapting the answer from there.
Suppose this is your original code you were trying to make work:
// In your "calling code" (inside onCreate() or some click listener):
val db = FirebaseFirestore.getInstance()
val first = db.collection("Post").orderBy("timestamp")
val postSize = getPostSize(first)
// do something with postSize
// Elsewhere in your class:
private fun getPostSize(first: Query): Int {
var postSize = 0
first.get().addOnSuccessListener { documents ->
for(document in documents) {
Log.d(TAG, "${document.id} => ${document.data}")
postSize++
}
}
return postSize
}
The reason this doesn't work is that the code inside your addOnSuccessListener is called some time in the future, after getPostSize() has already returned.
The reason asynchronous code is called in the future is because it takes a long time to do its action, but it's bad to wait for it on the calling thread because it will freeze your UI and make the whole phone unresponsive. So the time-consuming action is done in the background on another thread, which allows the calling code to continue doing what it's doing and finish immediately so it doesn't freeze the UI. When the time-consuming action is finally finished, only then is its callback/lambda code executed.
A simple retrieval from Firebase like this likely takes less than half a second, but this is still too much time to freeze the UI, because it would make the phone seem janky. Half a second in the future is still in the future compared to the code that is called underneath and outside the lambda.
For the sake of simplifying the below examples, let's simplify your original function to avoid using the for loop, since it was unnecessary:
private fun getPostSize(first: Query): Int {
var postSize = 0
first.get().addOnSuccessListener { documents ->
postSize = documents.count()
}
return postSize
}
The following are multiple distinct approaches for working with asynchronous code. You only have to pick one. You don't have to do all of them.
1. Make your function take a callback instead of returning a value.
Change you function into a higher order function. Since the function doesn't directly return the post size, it is a good convention to put "Async" in the function name. What this function does now is call the callback to pass it the value you wanted to retrieve. It will be called in the future when the listener has been called.
private fun getPostSizeAsync(first: Query, callback: (Int) -> Unit) {
first.get().addOnSuccessListener { documents ->
val postSize = documents.count()
callback(postSize)
}
}
Then to use your function in your "calling code", you must use the retrieved value inside the callback, which can be defined using a lambda:
// In your "calling code" (inside onCreate() or some click listener):
val db = FirebaseFirestore.getInstance()
val first = db.collection("Post").orderBy("timestamp")
getPostSizeAsync(first) { postSize ->
// do something with postSize inside the lambda here
}
// Don't try to do something with postSize after the lambda here. Code under
// here is called before the code inside the lambda because the lambda is called
// some time in the future.
2. Handle the response directly in the calling code.
You might have noticed in the above solution 1, you are really just creating an intermediate callback step, because you already have to deal with the callback lambda passed to addOnSuccessListener. You could eliminate the getPostSize function completely and just deal with callbacks at once place in your code. I wouldn't normally recommend this because it violates the DRY principle and the principle of avoiding dealing with multiple levels of abstraction in a single function. However, it may be better to start this way until you better grasp the concept of asynchronous code.
It would look like this:
// In your "calling code" (inside onCreate() or some click listener):
val db = FirebaseFirestore.getInstance()
val first = db.collection("Post").orderBy("timestamp")
first.get().addOnSuccessListener { documents ->
val postSize = documents.count()
// do something with postSize inside the lambda here
}
// Don't try to do something with postSize after the lambda here. Code under
// here is called before the code inside the lambda because the lambda is called
// some time in the future.
3. Put the result in a LiveData. Observe the LiveData separately.
You can create a LiveData that will update its observers about results when it gets them. This may not be a good fit for certain situations, because it would get really complicated if you had to turn observers on and off for your particular logic flow. I think it is probably a bad solution for your code because you might have different queries you want to pass to this function, so it wouldn't really make sense to have it keep publishing its results to the same LiveData, because the observers wouldn't know which query the latest postSize is related to.
But here is how it could be done.
private val postSizeLiveData = MutableLiveData<Int>()
// Function name changed "get" to "fetch" to reflect it doesn't return
// anything but simply initiates a fetch operation:
private fun fetchPostSize(query: Query) {
first.get().addOnSuccessListener { documents ->
postSize.value = documents.count()
}
}
// In your "calling code" (inside onCreate() or some click listener):
val db = FirebaseFirestore.getInstance()
val first = db.collection("Post").orderBy("timestamp")
fetchPostSize(first)
postSizeLiveData.observer(this) { postSize ->
// Do something with postSize inside this observer that will
// be called some time in the future.
}
// Don't try to do something with postSize after the lambda here. Code under
// here is called before the code inside the lambda because the lambda is called
// some time in the future.
4. Use a suspend function and coroutine.
Coroutines allow you to write synchronous code without blocking the calling thread. After you learn to use coroutines, they lead to simpler code because there's less nesting of asynchronous callback lambdas. If you look at option 1, it will become very complicated if you need to call more than one asynchronous function in a row to get the results you want, for example if you needed to use postSize to decide what to retrieve from Firebase next. You would have to call another callback-based higher-order function inside the lambda of your first higher-order function call, nesting the future code inside other future code. (This is nicknamed "callback hell".) To write a synchronous coroutine, you launch a coroutine from lifecycleScope (or viewLifecycleOwner.lifecycleScope in a Fragment or viewModelScope in a ViewModel). You can convert your getter function into a suspend function to allow it to be used synchronously without a callback when called from a coroutine. Firebase provides an await() suspend function that can be used to wait for the result synchronously if you're in a coroutine. (Note that more properly, you should use try/catch when you call await() because it's possible Firebase fails to retrieve the documents. But I skipped that for simplicity since you weren't bothering to handle the possible failure with an error listener in your original code.)
private suspend fun getPostSize(first: Query): Int {
return first.get().await().count()
}
// In your "calling code" (inside onCreate() or some click listener):
lifecycleScope.launch {
val db = FirebaseFirestore.getInstance()
val first = db.collection("Post").orderBy("timestamp")
val postSize = getPostSize(first)
// do something with postSize
}
// Code under here will run before the coroutine finishes so
// typically, you launch coroutines and do all your work inside them.
Coroutines are the common way to do this in Kotlin, but they are a complex topic to learn for a newcomer. I recommend you start with one of the first two solutions until you are much more comfortable with Kotlin and higher order functions.

Kotlin-Firebase-cannot utilize the string read from database further down in the code

I have managed to read data from my firebase database but cant seem to re-use the String which has been read.
My successful read is as per below. When i check the logcat for the Log.d("Brand") it actually shows the String as expected.
brandchosenRef=FirebaseDatabase.getInstance().reference
val brandsRef = brandchosenRef.child("CarList2").orderByChild("Car").equalTo(searchable_spinner_brand.selectedItem.toString())
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for(ds in dataSnapshot.children){
Log.d("spinner brand",searchable_spinner_brand.selectedItem.toString())
val Brand = ds.child("Brand").getValue(String::class.java)
val brandselected= Brand.toString()
Log.d("Brand","$brandselected")
selectedbrand== brandselected
Log.d("selected brand",selectedbrand)
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("Branderror","error on brand")
}
}
brandsRef.addListenerForSingleValueEvent(valueEventListener)
What i am trying to do is write "selectedbrand" into a separate node using the following:
val carselected = searchable_spinner_brand.selectedItem.toString()
val dealref = FirebaseDatabase.getInstance().getReference("Deal_Summary2")
val dealsummayId = dealref.push().key
val summaryArray = DealSummaryArray(dealsummayId.toString(),"manual input for testing","brand","Deal_ID",carselected,extrastext.text.toString(),otherinfo.text.toString(),Gauteng,WC,KZN,"Open")
dealref.child(dealsummayId.toString()).setValue(summaryArray).addOnCompleteListener{
}
Note, in the above i was inputting "manual input for testing" to check that my write to Firebase was working and it works as expected. if i replace that with selectedbrand, then i get the below error.
kotlin.UninitializedPropertyAccessException: lateinit property selectedbrand has not been initialized
the summary array indicated above is defined in a separate class as follows. and as seen "manual input for testing is declared as String.
class DealSummaryArray(val id:String,val brand:String,val Buyer_ID:String,val Deal_ID:String,val Car:String,val extras:String,val other_info:String,val Gauteng:String,val Western_Cape:String,val KZN:String,val Status:String) {
constructor():this("","","","","","","","","","",""){
}
}
My question simply put, it why can i not re-use the value i read from the database? even if i was not trying to re-write it to a new node i cannot seem to utilize the value outside of the firebase query.
I seem to get this problem everywhere in my activities and have to find strange work around's like write to a textview and then reference the textview. please assist.
Data is loaded from Firebase asynchronously, as it may take some time before you get a response from the server. To prevent blocking the application (which would be a bad experience for your users), your main code continues to run while the data is being loaded. And then when the data is available, Firebase calls your onDataChange method.
What this means in practice is that any code that needs the data from the database, needs to be inside the onDataChange method or be called from there. So any code that requires selectedbrand needs to be inside onDataChange or called from there (typically through a callback interface).
Also see:
How to check a certain data already exists in firestore or not, which contains example code including of the callback interface, in Java.
getContactsFromFirebase() method return an empty list, which contains a similar example for the Firebase Realtime Database.
Setting Singleton property value in Firebase Listener, which shows a way to make the code behave more synchronous, and explains shows that this may not work on various Android versions.

ModelBuilder end() not working as it should "com.badlogic.gdx.utils.GdxRuntimeException: Call end() first"

I'm just trying to get a basic box rendered with LibGDX (using Kotlin & LibKTX) but am running into some issues.
If I call the ModelBuilder createBox function without the specified begin() and end() functions my box is not rendered. I checked the materials, camera position, the bounding box, added a light source, etc. but it's just.. not there. I figured the issue was with the way I was building the nodes, as I can't find an issue with the Material.
This is how I am trying to render my box:
class HomeView(private val baseUI: BaseUI) : KtxScreen {
private val cam by lazy { PerspectiveCamera(67f, baseUI.aWidth, baseUI.aHeight) }
private val boxInstance: ModelInstance
private val modelBatch: ModelBatch
private val modelBuilder: ModelBuilder by lazy { ModelBuilder() }
private val vertexAttributes =
VertexAttributes.Usage.Position.toLong() or
VertexAttributes.Usage.Normal.toLong() or
VertexAttributes.Usage.TextureCoordinates.toLong()
private val greenMat by lazy { Material(ColorAttribute.createDiffuse(Color.GREEN)) }
private val environment by lazy { Environment() }
//------ end global
init {
cam.position.set(vec3(-10f, -10f, 10f))
cam.lookAt(0f, 0f, 0f)
cam.near = .1f
cam.far = 10f
cam.update()
modelBatch = ModelBatch()
modelBuilder.begin()
modelBuilder.createBox(5f, 5f, 5f, greenMat, vertexAttributes)
boxInstance = ModelInstance(modelBuilder.end())
environment.set(ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f))
environment.add(DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f))
}
override fun render(delta: Float) {
Gdx.gl.glViewport(0, 0, baseUI.aWidth.toInt(), baseUI.aHeight.toInt())
//this is working, screen appears grey
clearScreen(.3f, .3f, .3f, 1f)
modelBatch.begin(cam)
modelBatch.render(boxInstance, environment)
modelBatch.end()
}
//rest omitted
}
and here is my BaseUI class that I'm using to add the screen (I'm just trying to test screens out, this is all just for testing purposes so ignore the inefficiency please)
class BaseUI : KtxGame<KtxScreen>(), KtxApplicationAdapter {
val aWidth by lazy { Gdx.graphics.width.toFloat() }
val aHeight by lazy { Gdx.graphics.height.toFloat() }
override fun create() {
addScreen(HomeView(this))
setScreen<HomeView>()
}
override fun render() {
super<KtxGame>.render()
}
//rest ommitted
}
When I run this I get the following error:
Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: Call end() first
which makes it seem like I need to call modelBatch.end() before I even create the nodes, which is confusing. I feel like I am doing something very basic wrong here, as I was able to get the basic 3D examples working back when I was trying this with Java a few years back.
So, two questions:
Why is LibGDX saying that I need to call end() before I create the nodes with ModelBuilder?
Is using modelBuilder.begin() and modelBuilder.end() actually the best way to use the ModelBuilder? I've yet to see a 3D example do this. Admittedly, all the 3D examples I have found have been from like 2013 so this might just be something that's been added. The LibGDX 3D section says to use this set of tutorials that do not use the begin() and end() functions, so I'm a bit confused as to what is the "best practice".
Thanks for any help!
edit: I tried it with a loaded model and it's having the same issue. Hmm..
edit2: Thank you Xoppa for helping me figure out what was wrong. The LibKTX specific function clearScreen() did not incorporate the GL20.GL_COLOR_BUFFER_BIT clear function as expected, which was my bad from reading their documentation. Adding this to my render function displayed the green box as expected:
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT)
LibGDX is not saying to call end() before you create a node, but that there is already an existing model that was created. Therefore, before creating another one you need to call end().
From your code sample, the reason why GdxRuntimeException is being thrown is that you call begin() and then createBox(). createBox() actually calls begin() in the function. (take a look here: https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g3d/utils/ModelBuilder.java) Therefore, the next begin() call the model is not null and the exception is thrown.
For best practices, if createBox() can satisfy your request than just use that. If you need something more complicated, such that createSphere(), createCapsule() don't work for you, then you need to call begin(), part(...), and end().
Hope this helps!

How to inform a Flux that I have an item ready to publish?

I am trying to make a class that would take incoming user events, process them and then pass the result to whoever subscribed to it:
class EventProcessor
{
val flux: Flux<Result>
fun onUserEvent1(e : Event)
{
val result = process(e)
// Notify flux that I have a new result
}
fun onUserEvent2(e : Event)
{
val result = process(e)
// Notify flux that I have a new result
}
fun process(e : Event): Result
{
...
}
}
Then the client code can subscribe to EventProcessor::flux and get notified each time a user event has been successfully processed.
However, I do not know how to do this. I tried to construct the flux with the Flux::generate function like this:
class EventProcessor
{
private var sink: SynchronousSink<Result>? = null
val flux: Flux<Result> = Flux.generate{ sink = it }
fun onUserEvent1(e : Event)
{
val result = process(e)
sink?.next(result)
}
fun onUserEvent2(e : Event)
{
val result = process(e)
sink?.next(result)
}
....
}
But this does not work, since I am supposed to immediately call next on the SynchronousSink<Result> passed to me in Flux::generate. I cannot store the sink as in the example:
reactor.core.Exceptions$ErrorCallbackNotImplemented:
java.lang.IllegalStateException: The generator didn't call any of the
SynchronousSink method
I was also thinking about the Flux::merge and Flux::concat methods, but these are static and they create a new Flux. I just want to push things into the existing flux, such that whoever holds it, gets notified.
Based on my limited understanding of the reactive types, this is supposed to be a common use case. Yet I find it very difficult to actually implement it. This brings me to a suspicion that I am missing something crucial or that I am using the library in an odd way, in which it was not intended to be used. If this is the case, any advice is warmly welcome.