How to block UI until Observable gets a response or timeout? - android-jetpack-compose

In my Android project I have a simple switch with a boolean interface to be imlemented later:
Switch (
text: String,
val isSwitchChecked: (Boolean) -> Unit
)
which I use like:
Switch("Measure Temperature"){ it: Boolean ->
Observable
.transmitToBackend(it)
.doOnNext{
Toast(it)
}
.onErrorComplete{
//..stuff
}
.subscribe()
}
The widget is a switch, can be turned on and off. When operating the switch, I would like to send the new value to the backend to have it stored there; what is working so far with .updateToBankend(it).
But when I tap consequently on the switch, it confuses the widget. To prohibit fast tappings on the switch, how can I block the switch UI until timeout or response? debounce() ?

Related

Jetpack Compose - Processing after completion of composition [duplicate]

I'm currently studying Jetpack Compose in an attempt to build a feature-rich application using modern Android architecture components. Traditionally, each screen (or navigation unit) in my application would be either an activity or a fragment, each with its own lifecycle bindings, but with Jetpack Compose and the Compose Navigation library, I would do something like this:
MainActivity.kt:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "main") {
composable("main") { MainScreen(navController) }
// More composable calls
}
}
}
}
Where MainScreen is just a composable. My questions are:
What is the equivalent here of a "lifecycle" for this composable? Say I want to perform some action when the screen is loaded, when it is destroyed etc. This is perhaps more relevant to the case where I have more screens and navigation between them
Is there some way to integrate between Compose and standard activities? That is, define activities for screens as you would, with each activity being a ComponentActivity and defining its own composable layout? Is this discouraged for some reason?
The Compose application is designed to be used in a single-activity architecture with no fragments.
You can still have multiple activities or fragments and use setContent in each of them, but in this case the transfer of data between activities falls on your shoulders. Use this approach if you're adding new Compose screens to an existing application built the old way.
But with Compose, it's much easier to do all the navigation within a single activity using Compose Navigation. Much less code, better performance due to no unnecessary code layers, easy to transfer data, etc.
To work with the view lifecycle, check out compose side-effects:
LaunchedEffect can be used to execute an action when the view appears. It also runs on a coroutine context that is bound to the current composable: you can easily run suspend functions, and when the view disappears from view hierarchy - the coroutine will be canceled.
DisposableEffect can be used to subscribe to/unsubscribe from callbacks.
When you rotate the screen, all effects will restart no matter which key you passed.
#Composable
fun MainScreen(navController: NavController) {
LaunchedEffect(Unit) {
println("LaunchedEffect: entered main")
var i = 0
// Just an example of coroutines usage
// don't use this way to track screen disappearance
// DisposableEffect is better for this
try {
while (true) {
delay(1000)
println("LaunchedEffect: ${i++} sec passed")
}
} catch (cancel: CancellationException) {
println("LaunchedEffect: job cancelled")
}
}
DisposableEffect(Unit) {
println("DisposableEffect: entered main")
onDispose {
println("DisposableEffect: exited main")
}
}
}
Also note that in both cases, and in many other cases in compose, you pass key to these functions. This helps compose understand when the value should be recomputed. In my example it is Unit, which means that it won't change until the view is gone. But if you create a remember value, use another dynamic value from the view model, or pass another argument to composable, you can pass it as a key, this will cancel the current LaunchedEffect job and call onDispose for DisposableEffect, and your job will be restarted with the updated key value. You can pass as many keys as you want.
Read more about the state in Compose in documentation.

Reactor using map() on a Flux does not push elements but .flapMap() does

I suppose even after reading the javadocs multiple times I don't get the difference between map and flatMap apart from the synchronous vs asynchronous transforms.In the following code I don't get any events (it behaves as if the subscribe() was not there.
final Flux<GroupedFlux<String, TData>> groupedFlux =
flux.groupBy(Event::getPartitionKey);
groupedFlux.subscribe(g -> g.delayElements(Duration.ofMillis(100))
.map(this::doWork)
.doOnError(throwable -> log.error("error: ", throwable))
.onErrorResume(e -> Mono.empty())
.subscribe());
However a flapMap() works. This works fine -
final Flux<GroupedFlux<String, TData>> groupedFlux =
flux.groupBy(Event::getPartitionKey);
groupedFlux.subscribe(g -> g.delayElements(Duration.ofMillis(100))
.flatMap(this::doWork)
.doOnError(throwable -> log.error("error: ", throwable))
.onErrorResume(e -> Mono.empty())
.subscribe());
Why is that?
EDIT:
Added sample code for the doWork method as suggested in a comment.
private Mono<Object> doWork(Object event) {
// do some work and possibly return Mono<Object> or
return Mono.empty();
}
I think it's because your doWork method returns a Mono. The map operation implicitly wraps your returned object inside a Mono, so you get a Mono<Mono>. Since your original flow subscribes to the wrapper Mono, but the one inside that one is not subscribed to it never produces anything. In contrast flatMap needs the wrapping to be explicit.
Try modifing your doWork method to return not a Mono and do the explicit Mono.just in the flatMap operation.

RxSwift var-outlet binding organistaion

I'm try to use RxSwift in my code and have a question:
I have a Settings class with different properties (staticValue for example):
class DeviceSettings: NSObject {
var staticValue = Variable<Int>(0)
}
I have an UI with UITextField which should be two-way connected with property, which works fine:
settings.staticValue.asObservable()
.map({ String($0) })
.bindTo(staticValueField.rx.text)
.addDisposableTo(disposeBag)
staticValueField.rx.controlEvent([.editingDidEnd]).asObservable()
.map({ Int(self.staticValueField.text!)! }) // Always Int
.subscribe(onNext: {
self.settings.staticValue.value = $0
}).addDisposableTo(disposeBag)
But actual Settings object will be loaded via network request and can be reloaded several times during VC lifecycle.
So:
How I should store my settings object? As usual variable or as
Variable<Settings> with ? or !?
Is my two-way-binding correct?
In which order I need to load data and call bind logic to enable binding and set start value at once?
Storing as usual variable. All observable properties is inside and stored as Variable<T>
It seems binding is correct and all works fine for now
I performed network request
Stored received object in my Setting object variable. If I will need to refresh data, I will update object properties values, but NOT the full object
Added .startWith( settings.staticValue.value ) to value -> tf binding for initial value
P.S. Correct me, if I made some mistake

RxJava2 order of sequence called with compleatable andThen operator

I am trying to migrate from RxJava1 to RxJava2. I am replacing all code parts where I previously had Observable<Void> to Compleatable. However I ran into one problem with order of stream calls. When I previously was dealing with Observables and using maps and flatMaps the code worked 'as expected'. However the andthen() operator seems to work a little bit differently. Here is a sample code to simplify the problem itself.
public Single<String> getString() {
Log.d("Starting flow..")
return getCompletable().andThen(getSingle());
}
public Completable getCompletable() {
Log.d("calling getCompletable");
return Completable.create(e -> {
Log.d("doing actuall completable work");
e.onComplete();
}
);
}
public Single<String> getSingle() {
Log.d("calling getSingle");
if(conditionBasedOnActualCompletableWork) {
return getSingleA();
}else{
return getSingleB();
}
}
What I see in the logs in the end is :
1-> Log.d("Starting flow..")
2-> Log.d("calling getCompletable");
3-> Log.d("calling getSingle");
4-> Log.d("doing actuall completable work");
And as you can probably figure out I would expect line 4 to be called before line 3 (afterwards the name of andthen() operator suggest that the code would be called 'after' Completable finishes it's job). Previously I was creating the Observable<Void> using the Async.toAsync() operator and the method which is now called getSingle was in flatMap stream - it worked like I expected it to, so Log 4 would appear before 3. Now I tried changing the way the Compleatable is created - like using fromAction or fromCallable but it behaves the same. I also couldn't find any other operator to replace andthen(). To underline - the method must be a Completable since it doesn't have any thing meaning full to return - it changes the app preferences and other settings (and is used like that globally mostly working 'as expected') and those changes are needed later in the stream. I also tried to wrap getSingle() method to somehow create a Single and move the if statement inside the create block but I don't know how to use getSingleA/B() methods inside there. And I need to use them as they have their complexity of their own and it doesn't make sense to duplicate the code. Any one have any idea how to modify this in RxJava2 so it behaves the same? There are multiple places where I rely on a Compleatable job to finish before moving forward with the stream (like refreshing session token, updating db, preferences etc. - no problem in RxJava1 using flatMap).
You can use defer:
getCompletable().andThen(Single.defer(() -> getSingle()))
That way, you don't execute the contents of getSingle() immediately but only when the Completablecompletes and andThen switches to the Single.

Luaj - add JButton action listener from Lua

In the application I'm developing in Java SE I use Luaj to implement functionality (this is a data collector application). The Java app reads a COM port of a device and gives the data to Lua event handlers which are written by the user of the application. Part of the user interface is also constructed from Lua, however, I'm having problems adding ActionListener objects (implemented in Lua as well) to Swing components, like JButton.
The code I'm currenty stuck at:
button = luajava.newInstance("javax.swing.JButton","test")
visuals:getPanel():add(button)
This creates a JButton object and puts it on a JPanel component. I'd like to define the action listener for this button in Lua as well.
Any idea how I can do that?
I tried the following, but it obviously does not work.
al = {}
function al.actionPerformed(ev)
print("test")
end
button.addActionListener(al)
I come a bit late, but for the reference, the swingapp.lua script shows how to handle listeners:
button:addActionListener(luajava.createProxy('java.awt.event.ActionListener',
{
actionPerformed = function (e)
print('Action', e)
end,
}))
Tested with Luaj-jse 3.0-alpha1

Resources