This is primarily a bug fix release, which addresses some issues which were recently discovered and improves things here and there. It is compatible to version 0.6.0.
For detailed release notes, you can find the changelog here.
This is primarily a bug fix release, which addresses some issues which were recently discovered and improves things here and there. It is compatible to version 0.6.0.
For detailed release notes, you can find the changelog here.
This version is an important milestone because it’s the first version which is written solely for Java 8!
In particular that means, it leverages many of the new Java 8 goodies, most notably the new Date/Time API, Functional Interfaces (e.g. Predicate
) and Lambda Expressions and paves the way for future improvements (e.g. Stream API) to keep the library modern.
The legacy Date API (java.util.Date/Calendar/TimeZone
) has been replaced by the new java.time
package, i.e. Instant
, OffsetDateTime
and ZoneOffset
.
Event Listeners have been replaced by java.util.function.Consumer
, which really saved a lot of similar interfaces and thanks to lambdas there isn’t even a syntactical difference.
Further new features are:
The updated documentation can be found here.
Support for XEP-0198: Stream Management is nearly done, but still needs some polishing, so it’s postponed to the next version.
Thanks for everybody who provided feedback and bug reports! I’m happy to hear more from you!
It shows how it could look like when two XMPP contacts are chatting with each other and having real-time text enabled. Enjoy!
Roughly speaking you have to create a new outbound real-time message and just update its text while typing:
// Create a chat session with another user. Chat chat = xmppClient.getManager(ChatManager.class).createChatSession(contact); RealTimeTextManager realTimeTextManager = xmppClient.getManager(RealTimeTextManager.class); // Create an new RTT message. OutboundRealTimeMessage realTimeMessage = realTimeTextManager.createRealTimeMessage(chat); TextArea textArea = new TextArea(); textArea.textProperty().addListener((observable, oldValue, newValue) -> { realTimeMessage.update(newValue); });
When done typing, commit the message (which will send the current message as normal chat message):
realTimeMessage.commit();
On the receiver side, you can listen for it like this (and e.g. display it to a Label).
Label label = new Label(); // Upon receiving a RTT message, display it. realTimeTextManager.addRealTimeMessageListener(e -> { InboundRealTimeMessage rtt = e.getRealTimeMessage(); rtt.addRealTimeTextChangeListener(e1 -> Platform.runLater(() -> label.setText(e1.getText()))); });
This release contains some new features, most noteworthy:
CallbackHandler
IQHander
interfaceA more comprehensive list can be found in the changelog.
Special thanks goes to Markus Karg, who has provided many good ideas and test results, issued bugs and suggested improvements!
0.5.0 will be the last release for Java 7, future releases will be for Java 8.
The reasons behind this decision are:java.time
and java.util.function
packages and the new Async API.I only want to give a vague preview here, but my plans for new features are currently to implement XEP-0198: Stream Management and finally finish XEP-0301: In-Band Real Time Text.
Happy coding!Chat States. Everybody knows them from Instant Messengers or Facebook Chat. Those tiny notifications, which tell you, if your chat partner is currently composing a message, is (in)active or has paused typing. E.g. "XY is typing..."
XMPP defines these states in XEP-0085: Chat State Notifications as:Each of them - except "gone" - are applicable to a "message input interface". So let's translate them to a JavaFX TextArea!
First we define, that whenever the TextArea receives focus, we want to change the state from 'inactive' (which is the initial default state) to 'active' (if there's no text) or to 'paused' (if there's already text):
Secondly we have to change to 'composing', whenever the text changes. Easy.
The slightly tricky part is to change to the 'paused' state. To achieve this, we can set up a javafx.animation.PauseTransition
and restart it everytime the text or focus has changed. Eventually, when the transition has finished (e.g. after 3 seconds), it will automatically change the state to 'paused':
Lastly, we change to 'inactive' when focus is lost:
And here's my take on a simple implementation. Enjoy!
import javafx.animation.PauseTransition; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.control.TextArea; import javafx.util.Duration; import rocks.xmpp.extensions.chatstates.model.ChatState; public class ChatStateTextArea extends TextArea { private final PauseTransition pauseTransition = new PauseTransition(Duration.seconds(3)); private final ReadOnlyObjectWrapper<ChatState> chatState = new ReadOnlyObjectWrapper<>(); public ChatStateTextArea() { // This is the initial state. chatState.set(ChatState.INACTIVE); focusedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean aBoolean2) { if (aBoolean2) { if (getText().isEmpty()) { // If we have received focus in an empty text field, immediately transition to "active". chatState.set(ChatState.ACTIVE); pauseTransition.stop(); } else { // If we have received focus in an non-empty text field, transition to "paused". chatState.set(ChatState.PAUSED); // Start the timer, which will automatically transition to the next state. pauseTransition.playFromStart(); } } else { pauseTransition.playFromStart(); } } }); textProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> observableValue, String s, String s2) { // We are in "composing" state. chatState.set(ChatState.COMPOSING); // Restart the timer. pauseTransition.playFromStart(); } }); pauseTransition.setOnFinished(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent actionEvent) { // When the time is up, switch to "paused", if there's any text, otherwise to active. if (isFocused()) { if (getText() != null && !getText().isEmpty()) { chatState.set(ChatState.PAUSED); } else { chatState.set(ChatState.ACTIVE); } } else { chatState.set(ChatState.INACTIVE); } } }); } // Ommitted getters and setters for clearness. }