Enabling login button using RxJava2 on Android with JDK8 - rx-android

I want to enable the login button when the username and password are long enough, using RxJava2, but I am getting errors.
final Button loginButton = (Button) view.findViewById(R.id.loginbutton);
final TextView usernameField = (TextView) view.findViewById(R.id.username_logindialog);
Observable<Boolean> userNameValid = RxTextView.textChangeEvents(usernameField)
.map(e -> e.text())
.map(inputText -> inputText.length() > 4);
final TextView passwordField = (TextView) view.findViewById(R.id.password_logindialog);
Observable<Boolean> passwordValid = RxTextView.textChangeEvents(passwordField)
.map(e -> e.text())
.map(inputText -> inputText.length() > 7);
Observable<Boolean> registerEnabled =
Observable.combineLatest(userNameValid, passwordValid, (a,b) -> a && b);
registerEnabled.registerObserver( enabled -> loginButton.setEnabled(enabled));
For both sections where you see map(inputText this is the error:
And the error for a && b is
There are other errors but I think the first two errors are the root cause, but I don't see what I should use instead of Observable.combineLatest now.

You can use this
emailChangeObservable = RxTextView.textChangeEvents(email);
passwordChangeObservable = RxTextView.textChangeEvents(password);
// force-disable the button
submitButton.setEnabled(false);
Observable.combineLatest(emailChangeObservable, passwordChangeObservable,
(emailObservable, passwordObservable) -> {
boolean emailCheck = emailObservable.text().length() >= 3;
boolean passwordCheck = passwordObservable.text().length() >= 3;
return emailCheck && passwordCheck;
}).subscribe(aBoolean -> {
submitButton.setEnabled(aBoolean);
});
// submit button will only be clickable if both forms have more than 3 characters each
Check here for source

Related

F# equivalent of C# async event handler in Xamarin iOS [duplicate]

How does one code an asynchronous WPF (or Windows Forms) event handler in F#? Specifically, is there any coding pattern that approximates C# 5's async and await?
Here is a complete C# WPF app:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
class Program
{
static int IncrementSlowly(int previous)
{
Thread.Sleep(3000);
if (previous == 2) throw new Exception("Oops!");
return previous + 1;
}
static async void btn_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
btn.IsEnabled = false;
try
{
var prev = (int)btn.Content;
btn.Content = await Task.Run(() => IncrementSlowly(prev));
}
catch (Exception ex)
{
btn.Content = ex.Message;
}
finally
{
btn.IsEnabled = true;
}
}
[STAThread]
static void Main(string[] args)
{
var btn = new Button() { Content = 0 };
var win = new Window() { Content = btn };
btn.Click += btn_Click;
new Application().Run(win);
}
}
I am having trouble figuring out what the equivalent would be using F#. I have made several attempts using combinations of async workflows and Async methods. It just gets really messy real fast. I'm hoping there is an easy way that I'm just overlooking.
Here is my starting point, which locks up the UI at btn.Content <- incrementSlowly prev. What do I do next?
open System
open System.Threading
open System.Threading.Tasks
open System.Windows
open System.Windows.Controls
let incrementSlowly previous =
Thread.Sleep(3000)
if previous = 2 then failwith "Oops!"
previous + 1
let btn_Click (sender : obj) e =
let btn = sender :?> Button
btn.IsEnabled <- false
try
try
let prev = btn.Content :?> int
btn.Content <- incrementSlowly prev
with ex -> btn.Content <- ex.Message
finally
btn.IsEnabled <- true
[<EntryPoint>][<STAThread>]
let main _ =
let btn = new Button(Content = 0)
let win = new Window(Content = btn)
btn.Click.AddHandler(RoutedEventHandler(btn_Click))
Application().Run(win)
By the way, assume that incrementSlowly cannot be modified.
The first step is to make incrementSlowly asynchronous. This is actually synchronous in your C# code, which is probably not a good idea - in a realistic scenario, this could be communicating with network, so very often this can actually be asynchronous:
let incrementSlowly previous = async {
do! Async.Sleep(3000)
if previous = 2 then failwith "Oops!"
return previous + 1 }
Now, you can make the button click handler also asynchronous. We'll start it using Async.StartImmediate later to make sure that we can access UI elements, so we do not have to worry about dispatechers or UI threads for now:
let btn_Click (sender : obj) e = async {
let btn = sender :?> Button
btn.IsEnabled <- false
try
try
let prev = btn.Content :?> int
let! next = incrementSlowly prev
btn.Content <- next
with ex -> btn.Content <- ex.Message
finally
btn.IsEnabled <- true }
The final step is to change the event registration. Something like this should do the trick:
btn.Click.Add(RoutedEventHandler(fun sender e ->
btn_Click sender e |> Async.StartImmediate)
The key thing is Async.StartImmediate which starts the asynchronous workflow. When we call this on the UI thread, it ensures that all the actual work is done on the UI thread (unless you offload it explicitly to background) and so it is safe to access UI elements in your code.
Tomas correctly points out that if you can convert the slow method to be asynchronous, then let! and Async.StartImmedate work beautifully. That is preferred.
However, some slow methods do not have asynchronous counterparts. In that case, Tomas's suggestion of Async.AwaitTask works too. For completeness I mention another alternative, manually managing the marshalling with Async.SwitchToContext.
Async.AwaitTask a new Task
let btn_Click (sender : obj) e =
let btn = sender :?> Button
btn.IsEnabled <- false
async {
try
try
let prev = btn.Content :?> int
let! next = Task.Run(fun () -> incrementSlowly prev) |> Async.AwaitTask
btn.Content <- next
with ex -> btn.Content <- ex.Message
finally
btn.IsEnabled <- true
}
|> Async.StartImmediate
Manually manage thread context
let btn_Click (sender : obj) e =
let btn = sender :?> Button
btn.IsEnabled <- false
let prev = btn.Content :?> int
let uiContext = SynchronizationContext.Current
async {
try
try
let next = incrementSlowly prev
do! Async.SwitchToContext uiContext
btn.Content <- next
with ex ->
do! Async.SwitchToContext uiContext
btn.Content <- ex.Message
finally
btn.IsEnabled <- true
}
|> Async.Start

ReactiveUI: When using ReactiveCommand.CreateFromObservable() the UI does not update until the command completes

I'm using ReactiveUI 7.0 with WPF and .NET 4.5.2.
I'm trying to create a ReactiveCommand from an Observable. The code DOES work, however, the UI doesn't update until the command is completed. I have a progress bar and a progress window that I want to update as the command runs. In addition, the UI is unresponsive while the ReactiveCommand is executing (I can't click on a cancel button or anything else for that matter). I'm hoping it's something I am overlooking and obvious to somebody smarter than me. Or maybe I'm just doing it wrong.
Thanks for looking.
Here is the relevant code:
My ViewModel declaration:
public ReactiveCommand<Unit, string> PerformSelectedOperationCommand { get; set; }
private StringBuilder sb;
Within my ViewModel constructor:
PerformSelectedOperationCommand = ReactiveCommand.CreateFromObservable(PerformOperationObservable,
this.WhenAnyValue(x => x.SelectedPath, x => x.TotalFilesSelected,
(x, y) => x != null && y > 0));
// listen to the messages and append to output
PerformSelectedOperationCommand.Subscribe(s =>
{
sb.AppendLine(s);
ProgressWindowOutput = sb.ToString();
});
And here is the Observable contained in my ViewModel which runs when clicking on the Go button (Notice it's modifying properties of my ViewModel):
private IObservable<string> PerformOperationObservable()
{
sb.Clear();
return Observable.Create<string>((o) =>
{
using (cts = new CancellationTokenSource())
{
// this creates a copy of the file list which will keep us safe
// if the user is clicking around on the UI and changing the list.
var selectedFiles = FileList.Where(x => x.IsChecked).ToArray();
int total = selectedFiles.Length;
int count = 0;
foreach (var file in selectedFiles)
{
ProgressBarMessage = $"Processing {count + 1} of {total}";
o.OnNext($"Processing file {file.Name}");
SelectedFileOperation.PerformOperation(file.FullPath);
count++;
PercentComplete = (int)(((double)count / total) * 100);
if (cts.IsCancellationRequested)
{
PercentComplete = 0;
ProgressBarMessage = string.Empty;
break;
}
}
ProgressBarMessage = string.Empty;
}
o.OnCompleted();
return Disposable.Empty;
});
}
Observable are inherently single threaded, you need to specify where to do the work. You could do this, I believe:
PerformSelectedOperationCommand
= ReactiveCommand.CreateFromObservable(
PerformOperationObservable.SubscribeOn(RxApp.TaskPoolScheduler), // Move subscription to task pool
this.WhenAnyValue(
x => x.SelectedPath,
x => x.TotalFilesSelected,
(x, y) => x != null && y > 0
)
);
// listen to the messages and append to output
PerformSelectedOperationCommand
.ObserveOnDispather() // Return to UI thread
.Subscribe(s =>
{
sb.AppendLine(s);
ProgressWindowOutput = sb.ToString();
});
You explicitly subscribe to the observable on the task pool, moving the work of the UI thread. Right before using the output, you return to the UI thread to be able to do updates on the UI.

Keyboard covers TextField

As the center node of a gluon view I have a scrollpane which contains several textfields in a vbox. When one of these textfields becomes the focusowner and the keyboard shows up, the textfield doesn't get repositioned according to the layout of the keyboard, so it is left covered by the keyboard.
I tried putting
android:windowSoftInputMode="adjustResize"
in the AndroidManifest, but without any success.
As a workaround I translate the y-coordinates of the covered textfield to the visible area. When you press the android back button to hide the keyboard, the textfields position will be reset to its original state. The issue I'm getting here is that I don't get an event for the android back button, no matter where I add the listener:
view.addEventFilter(MobileEvent.BACK_BUTTON_PRESSED, evt -> eventFilter);
MobileApplication.getInstance().getGlassPane().addEventFilter(MobileEvent.BACK_BUTTON_PRESSED, evt -> eventFilter);
Is there any possibility to handle the positioning of a node under the keyboard, or to get a reference to the keyboard itself?
Only layers get the MobileEvent.BACK_BUTTON_PRESSED event. One solution is to go native and use the Android API.
This is the solution I could come up with so far:
public class PositionAdjuster {
public static void main(String[] args) { launch(args); }
private static final float SCALE = FXActivity.getInstance().getResources().getDisplayMetrics().density;
private Node nodeToAdjust;
private ObservableValue<Node> focusOwner;
private ViewGroup viewGroup;
private Rect currentBounds;
private DoubleProperty height;
private OnGlobalLayoutListener layoutListener;
public PositionAdjuster(Node nodeToAdjust, ObservableValue<Node> focusOwner) {
this.nodeToAdjust = nodeToAdjust;
this.focusOwner = focusOwner;
initLayoutListener();
}
private void initLayoutListener() {
double screenHeight = MobileApplication.getInstance().getScreenHeight();
height = new SimpleDoubleProperty(screenHeight);
height.addListener((ov, n, n1) -> onHeightChanged(n, n1));
layoutListener = () -> height.set(getCurrentHeigt());
viewGroup = FXActivity.getViewGroup();
viewGroup.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
currentBounds = new Rect();
}
private float getCurrentHeigt() {
viewGroup.getRootView().getWindowVisibleDisplayFrame(currentBounds);
return currentBounds.height() / SCALE;
}
private void onHeightChanged(Number oldValue, Number newValue) {
double heightDelta = newValue.doubleValue() - oldValue.doubleValue();
if (heightDelta < 0) {
double maxY = getBoundsInScene(nodeToAdjust)).getMaxY();
double currentMaxY = heightDelta + maxY;
double result = currentMaxY- getBoundsInScene(focuseOwner.getValue()).getMaxY();
if (result < 0) {
nodeToAdjust.setTranslateY(result);
}
} else if (heightDelta > 0) {
nodeToAdjust.setTranslateY(0);
}
}
private Bounds getBoundsInScene(Node node) {
return node.localToScene(node.getBoundsInLocal());
}
public void removeListener() {
viewGroup.getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener);
}
}
EDIT:
I think this is a more straightforward approach. The previous version was dependent on the maxY of noteToAdjust to be equal to the height of the screen, not taking into account e.g. the presence of a bottomBar. Now the maxY position of the focusedNode is validated against the visible screen height, and the difference is used to reposition its parent.
public AndroidPositionAdjuster(Node parent, ObservableValue<Node> focusOwner) {
this.parent = parent;
this.focusOwner = focusOwner;
initLayoutListener();
}
private void onHeightChanged(Number oldHeight, Number newHeight) {
double heightDelta = newHeight.doubleValue() - oldHeight.doubleValue();
if (heightDelta < 0) {
double maxY = newHeight.doubleValue();
double focusedNodeY = getBoundsInScene(focusOwner.getValue()).getMaxY();
double result = maxY - focusedNodeY;
if (result < 0) {
parent.setTranslateY(result);
}
} else if (heightDelta > 0) {
parent.setTranslateY(0);
}
}

WP7 Popup not showing

In my app I want to display a simple string within a popup when the user clicks on an image. For this I added a Tap gesture listener to the image and within the handler I have the following code:
private void GestureListener_Tap( object sender, GestureEventArgs e )
{
var img = sender as Image;
if( img == null ) {
return;
}
Point pos = e.GetPosition( img );
string text = "I'm a popup!";
var popup = new Popup() {
Child = new Border() {
BorderBrush = new SolidColorBrush( Colors.LightGray ),
Child = new TextBlock() {
Text = text,
TextWrapping = TextWrapping.Wrap,
},
},
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalOffset = pos.X,
VerticalOffset = pos.Y,
Visibility = Visibility.Visible,
};
popup.IsOpen = true;
Debug.WriteLine( "GestureListener_Tap: " + text );
}
The call to WriteLine prints in the debugger output window but the popup doesn't get displayed. What am I doing wrong here?
Thanks for your help!
I tried your code and the Popup is displayed. I think the problem for you is the Position for the Image relative to the Mouse. Try to set another Background for the Parent Container and I think you'll see the Popup. You can also try to play around with
Point pos = e.GetPosition(null);
until you get the Position you require

Persistent Dashboard Widget Preferences

I'm building a Dashboard Widget and I'm attempting to store preferences that persist across "sessions" (i.e., a user closing the widget and opening it again).
I've tried:
function setEmail(event)
{
var preferenceKey = "email";
var preferenceValue = $F("email");
widget.setPreferenceForKey(preferenceValue, preferenceKey);
}
function getEmail() {
var preferenceForKey = "email";
preferenceForKey = widget.preferenceForKey(preferenceForKey);
return preferenceForKey;
}
This works fine for the current session, but if the widget is closed and opened again, the data is lost.
Thanks!
This seems to do the trick:
// Values you provide
var preferenceKey = "key"; // replace with the key for a preference
var preferenceValue = "value"; // replace with a preference to save
// Preference code
widget.setPreferenceForKey(preferenceValue, preferenceKey);

Resources