fix javafx thread errors and improve chat performance

This commit is contained in:
Arnaud Vergnet 2021-01-03 13:46:46 +01:00
parent e7a77a8670
commit e04b780397
5 changed files with 103 additions and 97 deletions

View file

@ -4,24 +4,36 @@ import fr.insa.clavardator.db.DatabaseController;
import fr.insa.clavardator.users.CurrentUser; import fr.insa.clavardator.users.CurrentUser;
import fr.insa.clavardator.users.PeerUser; import fr.insa.clavardator.users.PeerUser;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
public class ChatHistory { public class ChatHistory {
private final DatabaseController db; private final DatabaseController db;
private final PeerUser user; private final PeerUser user;
private final ObservableList<Message> history;
private final ArrayList<HistoryLoadedCallback> historyListener; private final ArrayList<HistoryLoadedCallback> historyListener;
private final ArrayList<MessageAddedCallback> messageListener; private final ArrayList<Message> fakeHistory;
private ArrayList<Message> history;
public ChatHistory(PeerUser user) { public ChatHistory(PeerUser user) {
this.user = user; this.user = user;
db = new DatabaseController(); db = new DatabaseController();
this.historyListener = new ArrayList<>(); history = FXCollections.observableArrayList();
this.messageListener = new ArrayList<>(); historyListener = new ArrayList<>();
fakeHistory = new ArrayList<>();
CurrentUser currentUser = CurrentUser.getInstance();
try {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
fakeHistory.add(new Message(user, currentUser, format.parse("2021-01-01 10:00:00"), "Coucou toi"));
} catch (ParseException e) {
e.printStackTrace();
}
} }
public void addHistoryLoadedListener(HistoryLoadedCallback listener) { public void addHistoryLoadedListener(HistoryLoadedCallback listener) {
@ -32,67 +44,50 @@ public class ChatHistory {
historyListener.remove(listener); historyListener.remove(listener);
} }
public void addMessageAddedListener(MessageAddedCallback listener) {
messageListener.add(listener);
}
public void removeMessageAddedListener(MessageAddedCallback listener) {
messageListener.remove(listener);
}
private void notifyHistoryLoaded() { private void notifyHistoryLoaded() {
historyListener.forEach(l -> l.onHistoryLoaded(user, history)); historyListener.forEach(l -> l.onHistoryLoaded(user));
}
private void notifyMessageAdded(Message message) {
messageListener.forEach(l -> l.onMessageAdded(user, message));
} }
public void load() { public void load() {
if (history == null) { // db.getChatHistory(new Date(), new Date(), // TODO: put actual date
// TODO remove after tests
CurrentUser currentUser = CurrentUser.getInstance();
history = new ArrayList<>();
history.add(new Message(user, currentUser, new Date(), "Coucou toi"));
history.add(new Message(currentUser, user, new Date(),"Coucou " + user.getUsername()));
history.add(new Message(user, currentUser, new Date(),"oui"));
history.add(new Message(currentUser, user, new Date(),"merci"));
// db.getChatHistory(new Date(), new Date(), // TODO: put actual date
// newHistory -> { // newHistory -> {
// history = newHistory; // history = newHistory;
// notifyHistoryLoaded(); // notifyHistoryLoaded();
// }); // });
}
Timer t = new Timer(); Timer t = new Timer();
t.schedule(new TimerTask() { t.schedule(new TimerTask() {
@Override @Override
public void run() { public void run() {
Platform.runLater(() -> notifyHistoryLoaded()); Platform.runLater(() -> {
history.addAll(fakeHistory);
history.sort((message, t1) -> (int) (message.getDate().getTime() - t1.getDate().getTime()));
});
notifyHistoryLoaded();
t.cancel(); t.cancel();
} }
}, 1000); }, 1000);
} }
public ObservableList<Message> getHistory() {
return history;
}
/** /**
* @param message * @param message
*/ */
public void addMessage(Message message) { public void addMessage(Message message) {
db.addMessage(message, () -> { Platform.runLater(() -> history.add(message));
if (history == null) { // db.addMessage(message, () -> {
history = new ArrayList<>(); // if (history == null) {
} // history = new ArrayList<>();
history.add(message); // }
notifyMessageAdded(message); // history.add(message);
}); // notifyMessageAdded(message);
} // });
public interface MessageAddedCallback {
void onMessageAdded(PeerUser user, Message message);
} }
public interface HistoryLoadedCallback { public interface HistoryLoadedCallback {
void onHistoryLoaded(PeerUser user, ArrayList<Message> history); void onHistoryLoaded(PeerUser user);
} }
} }

View file

@ -7,16 +7,14 @@ import fr.insa.clavardator.ui.LoadingScreenController;
import fr.insa.clavardator.ui.NoSelectionModel; import fr.insa.clavardator.ui.NoSelectionModel;
import fr.insa.clavardator.users.PeerUser; import fr.insa.clavardator.users.PeerUser;
import fr.insa.clavardator.util.ErrorCallback; import fr.insa.clavardator.util.ErrorCallback;
import fr.insa.clavardator.util.Log;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections; import javafx.collections.ListChangeListener;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class ChatController implements Initializable { public class ChatController implements Initializable {
@ -29,13 +27,16 @@ public class ChatController implements Initializable {
private ChatHeaderController chatHeaderController; private ChatHeaderController chatHeaderController;
@FXML @FXML
private LoadingScreenController loadingController; private LoadingScreenController loadingController;
@FXML @FXML
private VBox chatContainer; private VBox chatContainer;
@FXML @FXML
private VBox emptyContainer; private VBox emptyContainer;
private PeerUser remoteUser; private PeerUser remoteUser;
private final ChatHistory.HistoryLoadedCallback onHistoryLoaded = (PeerUser user) -> {
if (user.equals(remoteUser)) {
setState(State.DONE);
}
};
public void addAttachmentListener(ButtonPressEvent listener) { public void addAttachmentListener(ButtonPressEvent listener) {
chatFooterController.addAttachmentListener(listener); chatFooterController.addAttachmentListener(listener);
@ -53,31 +54,25 @@ public class ChatController implements Initializable {
if (this.remoteUser != null) { if (this.remoteUser != null) {
final ChatHistory oldHistory = this.remoteUser.getHistory(); final ChatHistory oldHistory = this.remoteUser.getHistory();
oldHistory.removeHistoryLoadedListener(onHistoryLoaded); oldHistory.removeHistoryLoadedListener(onHistoryLoaded);
oldHistory.removeMessageAddedListener(onMessageAdded);
} }
final ChatHistory history = remoteUser.getHistory(); final ChatHistory history = remoteUser.getHistory();
history.addHistoryLoadedListener(onHistoryLoaded); history.addHistoryLoadedListener(onHistoryLoaded);
history.addMessageAddedListener(onMessageAdded); messageList.setItems(history.getHistory());
messageList.getItems().addListener((ListChangeListener<? super Message>) (c) -> {
c.next();
scrollToEnd();
});
history.load(); history.load();
this.remoteUser = remoteUser; this.remoteUser = remoteUser;
} }
private final ChatHistory.HistoryLoadedCallback onHistoryLoaded = (PeerUser user, ArrayList<Message> messages) -> { private void scrollToEnd() {
if (user.equals(remoteUser)) { final int size = messageList.getItems().size();
messageList.setItems(FXCollections.observableArrayList(messages)); if (size > 0) {
messageList.scrollTo(messageList.getItems().size() - 1); Platform.runLater(() -> messageList.scrollTo(size - 1));
setState(State.DONE);
} }
}; }
private final ChatHistory.MessageAddedCallback onMessageAdded = (PeerUser user, Message message) -> {
Log.v(this.getClass().getSimpleName(), "Message added: " + message.getText());
Platform.runLater(() -> {
messageList.getItems().add(message);
messageList.scrollTo(messageList.getItems().size() - 1);
});
};
private void setState(State state) { private void setState(State state) {
switch (state) { switch (state) {

View file

@ -1,31 +1,36 @@
package fr.insa.clavardator.ui.chat; package fr.insa.clavardator.ui.chat;
import fr.insa.clavardator.chat.Message; import fr.insa.clavardator.chat.Message;
import fr.insa.clavardator.users.CurrentUser;
import fr.insa.clavardator.users.User;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import java.io.IOException; import java.io.IOException;
public class MessageListItemCell extends ListCell<Message> { public class MessageListItemCell extends ListCell<Message> {
private MessageListItemController messageListItemController;
private Node view;
public MessageListItemCell() { public MessageListItemCell() {
setStyle("-fx-padding: 0px"); setStyle("-fx-padding: 0px");
FXMLLoader cellLoader = new FXMLLoader(getClass().getResource("messageListItem.fxml"));
try {
view = cellLoader.load();
messageListItemController = cellLoader.getController();
} catch (IOException e) {
e.printStackTrace();
}
} }
@Override @Override
protected void updateItem(Message item, boolean empty) { protected void updateItem(Message item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (item != null) { if (item == null || empty) {
try { setGraphic(null);
FXMLLoader cellLoader = new FXMLLoader(getClass().getResource("messageListItem.fxml")); } else {
setGraphic(cellLoader.load()); setGraphic(view);
final MessageListItemController userListItemController = cellLoader.getController(); messageListItemController.setMessage(item);
userListItemController.setMessage(item);
} catch (IOException e) {
e.printStackTrace();
}
} }
} }
} }

View file

@ -15,6 +15,8 @@ import java.util.ResourceBundle;
public class MessageListItemController implements Initializable { public class MessageListItemController implements Initializable {
private Message currentMessage;
@FXML @FXML
private VBox container; private VBox container;
@FXML @FXML
@ -28,14 +30,19 @@ public class MessageListItemController implements Initializable {
} }
public void setMessage(Message message) { public void setMessage(Message message) {
button.setText(message.getText()); if (!message.equals(currentMessage)) {
timestamp.setText(DateFormat.getTimeInstance().format(message.getDate())); currentMessage = message;
if (message.getSender().id == CurrentUser.getInstance().getId()) { button.setText(message.getText());
container.setAlignment(Pos.CENTER_RIGHT); timestamp.setText(DateFormat.getTimeInstance().format(message.getDate()));
button.getStyleClass().add("message-self"); if (message.getSender().id == CurrentUser.getInstance().getId()) {
} else { container.setAlignment(Pos.CENTER_RIGHT);
container.setAlignment(Pos.CENTER_LEFT); button.getStyleClass().remove("message-other");
button.getStyleClass().add("message-other"); button.getStyleClass().add("message-self");
} else {
container.setAlignment(Pos.CENTER_LEFT);
button.getStyleClass().remove("message-self");
button.getStyleClass().add("message-other");
}
} }
} }
} }

View file

@ -3,6 +3,7 @@ package fr.insa.clavardator.ui.users;
import fr.insa.clavardator.ui.UserSelectedEvent; import fr.insa.clavardator.ui.UserSelectedEvent;
import fr.insa.clavardator.users.PeerUser; import fr.insa.clavardator.users.PeerUser;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import java.io.IOException; import java.io.IOException;
@ -10,9 +11,18 @@ import java.io.IOException;
public class UserListItemCell extends ListCell<PeerUser> { public class UserListItemCell extends ListCell<PeerUser> {
private UserSelectedEvent listener; private UserSelectedEvent listener;
private UserListItemController userListItemController;
private Node view;
public UserListItemCell() { public UserListItemCell() {
setStyle("-fx-padding: 0px"); setStyle("-fx-padding: 0px");
FXMLLoader cellLoader = new FXMLLoader(getClass().getResource("userListItem.fxml"));
try {
view = cellLoader.load();
userListItemController = cellLoader.getController();
} catch (IOException e) {
e.printStackTrace();
}
} }
public void setOnUserSelectedListener(UserSelectedEvent listener) { public void setOnUserSelectedListener(UserSelectedEvent listener) {
@ -22,23 +32,17 @@ public class UserListItemCell extends ListCell<PeerUser> {
@Override @Override
protected void updateItem(PeerUser item, boolean empty) { protected void updateItem(PeerUser item, boolean empty) {
super.updateItem(item, empty); super.updateItem(item, empty);
if (!empty && item != null) { if (item == null || empty) {
final FXMLLoader cellLoader = new FXMLLoader(getClass().getResource("userListItem.fxml"));
try {
setGraphic(cellLoader.load());
UserListItemController userListItemController = cellLoader.getController();
userListItemController.setUser(item);
userListItemController.setOnPressListener(() -> {
if (listener != null) {
listener.onSelected(item);
}
});
userListItemController.setSelected(isSelected());
} catch (IOException e) {
e.printStackTrace();
}
} else {
setGraphic(null); setGraphic(null);
} else {
setGraphic(view);
userListItemController.setUser(item);
userListItemController.setOnPressListener(() -> {
if (listener != null) {
listener.onSelected(item);
}
});
userListItemController.setSelected(isSelected());
} }
} }
} }