Merge pull request #132 from informatik-ag-ngl/f/login_dialog
JavaFX LoginDialog
This commit is contained in:
		| @@ -1,7 +1,6 @@ | ||||
| package envoy.client.data; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.util.function.Function; | ||||
| import java.util.logging.Level; | ||||
|  | ||||
| @@ -110,11 +109,5 @@ public class ClientConfig extends Config { | ||||
| 	 *         the registration option | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public LoginCredentials getLoginCredentials() { | ||||
| 		try { | ||||
| 			return new LoginCredentials(getUser(), getPassword(), false); | ||||
| 		} catch (NoSuchAlgorithmException e) { | ||||
| 			return null; | ||||
| 		} | ||||
| 	} | ||||
| 	public LoginCredentials getLoginCredentials() { return new LoginCredentials(getUser(), getPassword(), false); } | ||||
| } | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| package envoy.client.event; | ||||
|  | ||||
| import envoy.event.Event; | ||||
|  | ||||
| /** | ||||
|  * This {@link Event} indicates that a handshake was completed successfully. | ||||
|  * | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>HandshakeSuccessfulEvent.java</strong><br> | ||||
|  * Created: <strong>8 Feb 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class HandshakeSuccessfulEvent extends Event.Valueless { | ||||
|  | ||||
| 	private static final long serialVersionUID = 0L; | ||||
| } | ||||
| @@ -46,9 +46,10 @@ public class Client implements Closeable { | ||||
| 	private volatile Set<? extends Contact>	contacts; | ||||
| 	private volatile boolean				rejected; | ||||
|  | ||||
| 	// Configuration and logging | ||||
| 	private static final ClientConfig	config	= ClientConfig.getInstance(); | ||||
| 	private static final Logger			logger	= EnvoyLog.getLogger(Client.class); | ||||
| 	// Configuration, logging and event management | ||||
| 	private static final ClientConfig	config		= ClientConfig.getInstance(); | ||||
| 	private static final Logger			logger		= EnvoyLog.getLogger(Client.class); | ||||
| 	private static final EventBus		eventBus	= EventBus.getInstance(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Enters the online mode by acquiring a user ID from the server. As a | ||||
| @@ -80,7 +81,7 @@ public class Client implements Closeable { | ||||
| 		// Register user creation processor, contact list processor and message cache | ||||
| 		receiver.registerProcessor(User.class, sender -> { this.sender = sender; contacts = sender.getContacts(); }); | ||||
| 		receiver.registerProcessor(Message.class, receivedMessageCache); | ||||
| 		receiver.registerProcessor(HandshakeRejectionEvent.class, evt -> { rejected = true; EventBus.getInstance().dispatch(evt); }); | ||||
| 		receiver.registerProcessor(HandshakeRejectionEvent.class, evt -> { rejected = true; eventBus.dispatch(evt); }); | ||||
|  | ||||
| 		rejected = false; | ||||
|  | ||||
| @@ -106,11 +107,12 @@ public class Client implements Closeable { | ||||
| 			Thread.sleep(500); | ||||
| 		} | ||||
|  | ||||
| 		logger.info("Handshake completed."); | ||||
| 		online = true; | ||||
|  | ||||
| 		// Remove user creation processor | ||||
| 		receiver.removeAllProcessors(); | ||||
|  | ||||
| 		logger.info("Handshake completed."); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -146,20 +148,19 @@ public class Client implements Closeable { | ||||
| 		receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator); | ||||
|  | ||||
| 		// Process name changes | ||||
| 		receiver.registerProcessor(NameChangeEvent.class, evt -> { localDB.replaceContactName(evt); EventBus.getInstance().dispatch(evt); }); | ||||
| 		receiver.registerProcessor(NameChangeEvent.class, evt -> { localDB.replaceContactName(evt); eventBus.dispatch(evt); }); | ||||
|  | ||||
| 		// Process contact searches | ||||
| 		receiver.registerProcessor(ContactSearchResult.class, EventBus.getInstance()::dispatch); | ||||
| 		receiver.registerProcessor(ContactSearchResult.class, eventBus::dispatch); | ||||
|  | ||||
| 		receiver.registerProcessor(Contact.class, | ||||
| 				contacts -> EventBus.getInstance() | ||||
| 					.dispatch(new ContactOperationEvent(contacts.getContacts().iterator().next(), ElementOperation.ADD))); | ||||
| 				contacts -> eventBus.dispatch(new ContactOperationEvent(contacts.getContacts().iterator().next(), ElementOperation.ADD))); | ||||
|  | ||||
| 		// Process group size changes | ||||
| 		receiver.registerProcessor(GroupResizeEvent.class, evt -> { localDB.updateGroup(evt); EventBus.getInstance().dispatch(evt); }); | ||||
| 		receiver.registerProcessor(GroupResizeEvent.class, evt -> { localDB.updateGroup(evt); eventBus.dispatch(evt); }); | ||||
|  | ||||
| 		// Send event | ||||
| 		EventBus.getInstance().register(SendEvent.class, evt -> { | ||||
| 		eventBus.register(SendEvent.class, evt -> { | ||||
| 			try { | ||||
| 				sendEvent(evt.get()); | ||||
| 			} catch (IOException e) { | ||||
|   | ||||
							
								
								
									
										62
									
								
								src/main/java/envoy/client/ui/LoginDialog.fxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/main/java/envoy/client/ui/LoginDialog.fxml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
|  | ||||
| <?import javafx.scene.control.Button?> | ||||
| <?import javafx.scene.control.CheckBox?> | ||||
| <?import javafx.scene.control.DialogPane?> | ||||
| <?import javafx.scene.control.Label?> | ||||
| <?import javafx.scene.control.PasswordField?> | ||||
| <?import javafx.scene.control.TextField?> | ||||
| <?import javafx.scene.layout.ColumnConstraints?> | ||||
| <?import javafx.scene.layout.GridPane?> | ||||
| <?import javafx.scene.layout.HBox?> | ||||
| <?import javafx.scene.layout.RowConstraints?> | ||||
| <?import javafx.scene.layout.VBox?> | ||||
| <?import javafx.scene.text.Font?> | ||||
|  | ||||
| <DialogPane prefHeight="201.0" prefWidth="525.0" | ||||
| 	xmlns="http://javafx.com/javafx/11.0.1" | ||||
| 	xmlns:fx="http://javafx.com/fxml/1"> | ||||
| 	<content> | ||||
| 		<VBox maxHeight="-Infinity" maxWidth="-Infinity" | ||||
| 			minHeight="-Infinity" minWidth="-Infinity" prefHeight="217.0" | ||||
| 			prefWidth="545.0"> | ||||
| 			<children> | ||||
| 				<Label text="User Login"> | ||||
| 					<font> | ||||
| 						<Font size="26.0" /> | ||||
| 					</font> | ||||
| 				</Label> | ||||
| 				<GridPane> | ||||
| 					<columnConstraints> | ||||
| 						<ColumnConstraints hgrow="SOMETIMES" | ||||
| 							minWidth="10.0" percentWidth="40.0" prefWidth="100.0" /> | ||||
| 						<ColumnConstraints hgrow="SOMETIMES" | ||||
| 							minWidth="10.0" prefWidth="100.0" /> | ||||
| 					</columnConstraints> | ||||
| 					<rowConstraints> | ||||
| 						<RowConstraints minHeight="10.0" prefHeight="30.0" | ||||
| 							vgrow="SOMETIMES" /> | ||||
| 						<RowConstraints minHeight="10.0" prefHeight="30.0" | ||||
| 							vgrow="SOMETIMES" /> | ||||
| 						<RowConstraints minHeight="10.0" prefHeight="30.0" | ||||
| 							vgrow="SOMETIMES" /> | ||||
| 					</rowConstraints> | ||||
| 					<children> | ||||
| 						<Label text="User Name:" /> | ||||
| 						<Label text="Password" GridPane.rowIndex="1" /> | ||||
| 						<Label fx:id="repeatPasswordLabel" text="Repeat Password:" | ||||
| 							visible="false" GridPane.rowIndex="2" /> | ||||
| 						<TextField fx:id="userTextField" | ||||
| 							GridPane.columnIndex="1" /> | ||||
| 						<PasswordField fx:id="passwordField" | ||||
| 							GridPane.columnIndex="1" GridPane.rowIndex="1" /> | ||||
| 						<PasswordField fx:id="repeatPasswordField" | ||||
| 							visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2" /> | ||||
| 					</children> | ||||
| 				</GridPane> | ||||
| 				<CheckBox fx:id="registerCheckBox" mnemonicParsing="false" | ||||
| 					onAction="#registerCheckboxChanged" text="Register" /> | ||||
| 			</children> | ||||
| 		</VBox> | ||||
| 	</content> | ||||
| </DialogPane> | ||||
							
								
								
									
										162
									
								
								src/main/java/envoy/client/ui/LoginDialog.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/main/java/envoy/client/ui/LoginDialog.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| import javax.naming.TimeLimitExceededException; | ||||
|  | ||||
| import envoy.client.data.Cache; | ||||
| import envoy.client.data.ClientConfig; | ||||
| import envoy.client.data.LocalDB; | ||||
| import envoy.client.net.Client; | ||||
| import envoy.data.LoginCredentials; | ||||
| import envoy.data.Message; | ||||
| import envoy.data.User; | ||||
| import envoy.event.EventBus; | ||||
| import envoy.event.HandshakeRejectionEvent; | ||||
| import envoy.exception.EnvoyException; | ||||
| import envoy.util.EnvoyLog; | ||||
| import javafx.application.Platform; | ||||
| import javafx.event.ActionEvent; | ||||
| import javafx.fxml.FXML; | ||||
| import javafx.fxml.FXMLLoader; | ||||
| import javafx.scene.control.*; | ||||
| import javafx.scene.control.Alert.AlertType; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>LoginDialog.java</strong><br> | ||||
|  * Created: <strong>03.04.2020</strong><br> | ||||
|  *  | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public final class LoginDialog extends Dialog<Void> { | ||||
|  | ||||
| 	@FXML | ||||
| 	private TextField userTextField; | ||||
|  | ||||
| 	@FXML | ||||
| 	private PasswordField passwordField; | ||||
|  | ||||
| 	@FXML | ||||
| 	private PasswordField repeatPasswordField; | ||||
|  | ||||
| 	@FXML | ||||
| 	private Label repeatPasswordLabel; | ||||
|  | ||||
| 	@FXML | ||||
| 	private CheckBox registerCheckBox; | ||||
|  | ||||
| 	private final Client			client; | ||||
| 	private final LocalDB			localDB; | ||||
| 	private final Cache<Message>	receivedMessageCache; | ||||
|  | ||||
| 	private static final Logger			logger		= EnvoyLog.getLogger(LoginDialog.class); | ||||
| 	private static final EventBus		eventBus	= EventBus.getInstance(); | ||||
| 	private static final ClientConfig	config		= ClientConfig.getInstance(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Loads the login dialog using the FXML file {@code LoginDialog.fxml}. | ||||
| 	 *  | ||||
| 	 * @param client               the client used to perform the handshake | ||||
| 	 * @param localDB              the local database used for offline login | ||||
| 	 * @param receivedMessageCache the cache storing messages received during the | ||||
| 	 *                             handshake | ||||
| 	 * @throws IOException if an exception occurs during loading | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public LoginDialog(Client client, LocalDB localDB, Cache<Message> receivedMessageCache) throws IOException { | ||||
| 		this.client					= client; | ||||
| 		this.localDB				= localDB; | ||||
| 		this.receivedMessageCache	= receivedMessageCache; | ||||
|  | ||||
| 		final var loader = new FXMLLoader(getClass().getResource("LoginDialog.fxml")); | ||||
| 		loader.setController(this); | ||||
| 		final var dialogPane = loader.<DialogPane>load(); | ||||
|  | ||||
| 		// Configure dialog buttons | ||||
| 		dialogPane.getButtonTypes().addAll(ButtonType.CLOSE, ButtonType.OK); | ||||
|  | ||||
| 		// Close button | ||||
| 		dialogPane.lookupButton(ButtonType.CLOSE).addEventHandler(ActionEvent.ACTION, e -> abortLogin()); | ||||
|  | ||||
| 		// Login button | ||||
| 		final var loginButton = (Button) dialogPane.lookupButton(ButtonType.OK); | ||||
| 		loginButton.setText("Login"); | ||||
| 		loginButton.addEventFilter(ActionEvent.ACTION, e -> { | ||||
| 			e.consume(); | ||||
|  | ||||
| 			// Prevent registration with unequal passwords | ||||
| 			if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) { | ||||
| 				clearPasswordFields(); | ||||
| 				new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait(); | ||||
| 			} else | ||||
| 				performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected())); | ||||
| 		}); | ||||
|  | ||||
| 		// Perform automatic login if configured | ||||
| 		setOnShown(e -> { if (config.hasLoginCredentials()) performHandshake(config.getLoginCredentials()); }); | ||||
|  | ||||
| 		setDialogPane(dialogPane); | ||||
| 	} | ||||
|  | ||||
| 	@FXML | ||||
| 	private void initialize() { | ||||
|  | ||||
| 		// Show an alert after an unsuccessful handshake | ||||
| 		eventBus.register(HandshakeRejectionEvent.class, | ||||
| 				e -> Platform.runLater(() -> { clearPasswordFields(); new Alert(AlertType.ERROR, e.get()).showAndWait(); })); | ||||
|  | ||||
| 		// Set initial cursor | ||||
| 		userTextField.requestFocus(); | ||||
| 	} | ||||
|  | ||||
| 	@FXML | ||||
| 	private void registerCheckboxChanged() { | ||||
|  | ||||
| 		// Make repeat password field and label visible / invisible | ||||
| 		repeatPasswordField.setVisible(registerCheckBox.isSelected()); | ||||
| 		repeatPasswordLabel.setVisible(registerCheckBox.isSelected()); | ||||
|  | ||||
| 		clearPasswordFields(); | ||||
| 	} | ||||
|  | ||||
| 	private void abortLogin() { | ||||
| 		logger.info("The login process has been cancelled. Exiting..."); | ||||
| 		System.exit(0); | ||||
| 	} | ||||
|  | ||||
| 	private void performHandshake(LoginCredentials credentials) { | ||||
| 		try { | ||||
| 			client.performHandshake(credentials, receivedMessageCache); | ||||
| 			if (client.isOnline()) { | ||||
| 				client.initReceiver(localDB, receivedMessageCache); | ||||
| 				Platform.runLater(this::hide); | ||||
| 			} | ||||
| 		} catch (IOException | InterruptedException | TimeLimitExceededException e) { | ||||
| 			logger.warning("Could not connect to server. Trying offline mode..."); | ||||
| 			e.printStackTrace(); | ||||
| 			try { | ||||
| 				// Try entering offline mode | ||||
| 				localDB.loadUsers(); | ||||
| 				User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier()); | ||||
| 				if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); | ||||
| 				client.setSender(clientUser); | ||||
| 				Platform.runLater(() -> { | ||||
| 					new Alert(AlertType.WARNING, "A connection to the server could not be established. Starting in offline mode.\n" + e) | ||||
| 						.showAndWait(); | ||||
| 					hide(); | ||||
| 				}); | ||||
| 			} catch (Exception e1) { | ||||
| 				Platform.runLater(() -> new Alert(AlertType.ERROR, "Client error: " + e.toString()).showAndWait()); | ||||
| 				System.exit(1); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private void clearPasswordFields() { | ||||
| 		passwordField.clear(); | ||||
| 		repeatPasswordField.clear(); | ||||
| 	} | ||||
| } | ||||
| @@ -7,12 +7,9 @@ import java.util.Properties; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| import javax.swing.JOptionPane; | ||||
|  | ||||
| import envoy.client.data.*; | ||||
| import envoy.client.net.Client; | ||||
| import envoy.client.net.WriteProxy; | ||||
| import envoy.client.ui.container.LoginDialog; | ||||
| import envoy.data.Message; | ||||
| import envoy.data.User; | ||||
| import envoy.data.User.UserStatus; | ||||
| @@ -21,6 +18,8 @@ import envoy.util.EnvoyLog; | ||||
| import javafx.application.Application; | ||||
| import javafx.fxml.FXMLLoader; | ||||
| import javafx.scene.Scene; | ||||
| import javafx.scene.control.Alert; | ||||
| import javafx.scene.control.Alert.AlertType; | ||||
| import javafx.scene.image.Image; | ||||
| import javafx.scene.layout.GridPane; | ||||
| import javafx.stage.Stage; | ||||
| @@ -47,7 +46,7 @@ public final class Startup extends Application { | ||||
| 	 * {@inheritDoc} | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void init() throws Exception { | ||||
| 	public void start(Stage stage) throws Exception { | ||||
| 		try { | ||||
| 			// Load the configuration from client.properties first | ||||
| 			Properties properties = new Properties(); | ||||
| @@ -61,7 +60,7 @@ public final class Startup extends Application { | ||||
| 			// Check if all mandatory configuration values have been initialized | ||||
| 			if (!config.isInitialized()) throw new EnvoyException("Configuration is not fully initialized"); | ||||
| 		} catch (Exception e) { | ||||
| 			JOptionPane.showMessageDialog(null, "Error loading configuration values:\n" + e, "Configuration error", JOptionPane.ERROR_MESSAGE); | ||||
| 			new Alert(AlertType.ERROR, "Error loading configuration values:\n" + e); | ||||
| 			e.printStackTrace(); | ||||
| 			System.exit(1); | ||||
| 		} | ||||
| @@ -75,15 +74,12 @@ public final class Startup extends Application { | ||||
| 		// Initialize the local database | ||||
| 		if (config.isIgnoreLocalDB()) { | ||||
| 			localDB = new TransientLocalDB(); | ||||
| 			JOptionPane.showMessageDialog(null, | ||||
| 					"Ignoring local database.\nMessages will not be saved!", | ||||
| 					"Local database warning", | ||||
| 					JOptionPane.WARNING_MESSAGE); | ||||
| 			new Alert(AlertType.WARNING, "Ignoring local database.\nMessages will not be saved!").showAndWait(); | ||||
| 		} else try { | ||||
| 			localDB = new PersistentLocalDB(new File(config.getHomeDirectory(), config.getLocalDB().getPath())); | ||||
| 		} catch (IOException e3) { | ||||
| 			logger.log(Level.SEVERE, "Could not initialize local database", e3); | ||||
| 			JOptionPane.showMessageDialog(null, "Could not initialize local database!\n" + e3, "Local database error", JOptionPane.ERROR_MESSAGE); | ||||
| 			new Alert(AlertType.ERROR, "Could not initialize local database!\n" + e3).showAndWait(); | ||||
| 			System.exit(1); | ||||
| 			return; | ||||
| 		} | ||||
| @@ -93,7 +89,7 @@ public final class Startup extends Application { | ||||
| 		cache	= new Cache<>(); | ||||
|  | ||||
| 		// Try to connect to the server | ||||
| 		new LoginDialog(client, localDB, cache); | ||||
| 		new LoginDialog(client, localDB, cache).showAndWait(); | ||||
|  | ||||
| 		// Set client user in local database | ||||
| 		localDB.setUser(client.getSender()); | ||||
| @@ -106,10 +102,7 @@ public final class Startup extends Application { | ||||
| 			// The local database file has not yet been created, probably first login | ||||
| 		} catch (Exception e) { | ||||
| 			e.printStackTrace(); | ||||
| 			JOptionPane.showMessageDialog(null, | ||||
| 					"Error while loading local database: " + e + "\nChats will not be stored locally.", | ||||
| 					"Local DB error", | ||||
| 					JOptionPane.WARNING_MESSAGE); | ||||
| 			new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait(); | ||||
| 		} | ||||
|  | ||||
| 		// Initialize write proxy | ||||
| @@ -128,13 +121,6 @@ public final class Startup extends Application { | ||||
| 				.filter(u -> u instanceof User && u != localDB.getUser()) | ||||
| 				.map(User.class::cast) | ||||
| 				.forEach(u -> u.setStatus(UserStatus.OFFLINE)); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * {@inheritDoc} | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void start(Stage stage) throws Exception { | ||||
|  | ||||
| 		// Prepare stage and load ChatScene | ||||
| 		var	loader		= new FXMLLoader(getClass().getResource("ChatScene.fxml")); | ||||
|   | ||||
| @@ -1,346 +0,0 @@ | ||||
| package envoy.client.ui.container; | ||||
|  | ||||
| import java.awt.*; | ||||
| import java.awt.event.ItemEvent; | ||||
| import java.awt.event.WindowAdapter; | ||||
| import java.awt.event.WindowEvent; | ||||
| import java.io.IOException; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.util.Arrays; | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| import javax.naming.TimeLimitExceededException; | ||||
| import javax.swing.*; | ||||
| import javax.swing.border.EmptyBorder; | ||||
|  | ||||
| import envoy.client.data.*; | ||||
| import envoy.client.event.HandshakeSuccessfulEvent; | ||||
| import envoy.client.net.Client; | ||||
| import envoy.client.ui.Theme; | ||||
| import envoy.client.ui.primary.PrimaryButton; | ||||
| import envoy.data.LoginCredentials; | ||||
| import envoy.data.Message; | ||||
| import envoy.data.User; | ||||
| import envoy.event.EventBus; | ||||
| import envoy.event.HandshakeRejectionEvent; | ||||
| import envoy.exception.EnvoyException; | ||||
| import envoy.util.EnvoyLog; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>LoginDialog.java</strong><br> | ||||
|  * Created: <strong>01.01.2020</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class LoginDialog extends JDialog { | ||||
|  | ||||
| 	private JPanel			contentPanel; | ||||
| 	private JTextField		textField; | ||||
| 	private JPasswordField	passwordField; | ||||
| 	private JPasswordField	repeatPasswordField; | ||||
|  | ||||
| 	private JLabel	lblUserName; | ||||
| 	private JLabel	lblPassword; | ||||
| 	private JLabel	lblRepeatPassword; | ||||
| 	private JLabel	errorMessage; | ||||
|  | ||||
| 	private GridBagConstraints	gbc_lblRepeatPassword; | ||||
| 	private GridBagConstraints	gbc_repeatPasswordField; | ||||
| 	private GridBagConstraints	gbc_errorMessage; | ||||
|  | ||||
| 	private JPanel			buttonPane; | ||||
| 	private JTextPane		registerText; | ||||
| 	private JCheckBox		registerCheckBox; | ||||
| 	private PrimaryButton	okButton; | ||||
| 	private PrimaryButton	cancelButton; | ||||
|  | ||||
| 	private LoginCredentials credentials; | ||||
|  | ||||
| 	private final Client			client; | ||||
| 	private final LocalDB			localDB; | ||||
| 	private final Cache<Message>	receivedMessageCache; | ||||
|  | ||||
| 	private static final ClientConfig	config				= ClientConfig.getInstance(); | ||||
| 	private static final Logger			logger				= EnvoyLog.getLogger(LoginDialog.class); | ||||
| 	private static final long			serialVersionUID	= 0L; | ||||
|  | ||||
| 	/** | ||||
| 	 * Displays a dialog enabling the user to enter their user name and password. | ||||
| 	 * | ||||
| 	 * @param client               the client used to perform the handshake | ||||
| 	 * @param localDB              the local database in which data is persisted | ||||
| 	 * @param receivedMessageCache the cache that stored messages received during | ||||
| 	 *                             the handshake | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public LoginDialog(Client client, LocalDB localDB, Cache<Message> receivedMessageCache) { | ||||
| 		this.client					= client; | ||||
| 		this.localDB				= localDB; | ||||
| 		this.receivedMessageCache	= receivedMessageCache; | ||||
|  | ||||
| 		// Prepare handshake | ||||
| 		localDB.loadIDGenerator(); | ||||
|  | ||||
| 		addWindowListener(new WindowAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void windowClosing(WindowEvent e) { abortLogin(); } | ||||
| 		}); | ||||
|  | ||||
| 		initUi(); | ||||
|  | ||||
| 		okButton.addActionListener((evt) -> { | ||||
| 			try { | ||||
| 				if (registerCheckBox.isSelected()) { | ||||
| 					// Check password equality | ||||
| 					if (Arrays.equals(passwordField.getPassword(), repeatPasswordField.getPassword())) { | ||||
| 						credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), true); | ||||
| 						performHandshake(); | ||||
| 					} else { | ||||
| 						JOptionPane.showMessageDialog(this, "The repeated password is not the original password!"); | ||||
| 						clearPasswordFields(); | ||||
| 					} | ||||
| 				} else { | ||||
| 					credentials = new LoginCredentials(textField.getText(), passwordField.getPassword(), false); | ||||
| 					performHandshake(); | ||||
| 				} | ||||
| 			} catch (NoSuchAlgorithmException e) { | ||||
| 				e.printStackTrace(); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		// Listen to handshake rejections | ||||
| 		EventBus.getInstance() | ||||
| 			.register(HandshakeRejectionEvent.class, | ||||
| 					evt -> { clearPasswordFields(); errorMessage.setVisible(true); errorMessage.setText(evt.get()); }); | ||||
|  | ||||
| 		// Exit the application when the dialog is cancelled | ||||
| 		cancelButton.addActionListener(evt -> abortLogin()); | ||||
|  | ||||
| 		// Log in directly if configured | ||||
| 		if (config.hasLoginCredentials()) { | ||||
| 			credentials = config.getLoginCredentials(); | ||||
| 			performHandshake(); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		setVisible(true); | ||||
| 	} | ||||
|  | ||||
| 	private void performHandshake() { | ||||
| 		try { | ||||
| 			client.performHandshake(credentials, receivedMessageCache); | ||||
| 			if (client.isOnline()) { | ||||
| 				client.initReceiver(localDB, receivedMessageCache); | ||||
| 				dispose(); | ||||
| 			} | ||||
| 		} catch (IOException | InterruptedException | TimeLimitExceededException e) { | ||||
| 			logger.warning("Could not connect to server. Trying offline mode..."); | ||||
| 			e.printStackTrace(); | ||||
| 			try { | ||||
| 				// Try entering offline mode | ||||
| 				localDB.loadUsers(); | ||||
| 				User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier()); | ||||
| 				if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); | ||||
| 				client.setSender(clientUser); | ||||
| 				JOptionPane.showMessageDialog(null, | ||||
| 						"A connection to the server could not be established. Starting in offline mode.\n" + e, | ||||
| 						"Connection error", | ||||
| 						JOptionPane.WARNING_MESSAGE); | ||||
| 				dispose(); | ||||
| 			} catch (Exception e1) { | ||||
| 				JOptionPane.showMessageDialog(null, e1, "Client error", JOptionPane.ERROR_MESSAGE); | ||||
| 				System.exit(1); | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private void initUi() { | ||||
| 		setSize(338, 123); | ||||
| 		setLocationRelativeTo(null); | ||||
| 		setResizable(false); | ||||
| 		getContentPane().setLayout(new BorderLayout()); | ||||
| 		contentPanel = new JPanel(); | ||||
| 		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); | ||||
| 		getContentPane().add(contentPanel, BorderLayout.CENTER); | ||||
| 		GridBagLayout gbl_contentPanel = new GridBagLayout(); | ||||
| 		gbl_contentPanel.columnWidths	= new int[] { 0, 0, 0 }; | ||||
| 		gbl_contentPanel.rowHeights		= new int[] { 0, 0, 0 }; | ||||
| 		gbl_contentPanel.columnWeights	= new double[] { 0.0, 1.0, Double.MIN_VALUE }; | ||||
| 		gbl_contentPanel.rowWeights		= new double[] { 0.0, 0.0, Double.MIN_VALUE }; | ||||
| 		contentPanel.setLayout(gbl_contentPanel); | ||||
|  | ||||
| 		lblUserName = new JLabel("Username:"); | ||||
| 		GridBagConstraints gbc_lblUserName = new GridBagConstraints(); | ||||
| 		gbc_lblUserName.anchor	= GridBagConstraints.EAST; | ||||
| 		gbc_lblUserName.insets	= new Insets(0, 0, 5, 5); | ||||
| 		gbc_lblUserName.gridx	= 0; | ||||
| 		gbc_lblUserName.gridy	= 0; | ||||
| 		contentPanel.add(lblUserName, gbc_lblUserName); | ||||
|  | ||||
| 		textField = new JTextField(); | ||||
| 		textField.setBorder(null); | ||||
| 		GridBagConstraints gbc_textField = new GridBagConstraints(); | ||||
| 		gbc_textField.insets	= new Insets(0, 0, 5, 0); | ||||
| 		gbc_textField.fill		= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_textField.gridx		= 1; | ||||
| 		gbc_textField.gridy		= 0; | ||||
| 		contentPanel.add(textField, gbc_textField); | ||||
| 		textField.setColumns(10); | ||||
|  | ||||
| 		lblPassword = new JLabel("Password:"); | ||||
| 		GridBagConstraints gbc_lblPassword = new GridBagConstraints(); | ||||
| 		gbc_lblPassword.anchor	= GridBagConstraints.EAST; | ||||
| 		gbc_lblPassword.insets	= new Insets(0, 0, 0, 5); | ||||
| 		gbc_lblPassword.gridx	= 0; | ||||
| 		gbc_lblPassword.gridy	= 1; | ||||
| 		contentPanel.add(lblPassword, gbc_lblPassword); | ||||
|  | ||||
| 		passwordField = new JPasswordField(); | ||||
| 		passwordField.setBorder(null); | ||||
| 		GridBagConstraints gbc_passwordField = new GridBagConstraints(); | ||||
| 		gbc_passwordField.fill	= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_passwordField.gridx	= 1; | ||||
| 		gbc_passwordField.gridy	= 1; | ||||
| 		contentPanel.add(passwordField, gbc_passwordField); | ||||
|  | ||||
| 		lblRepeatPassword				= new JLabel("Repeat Password:"); | ||||
| 		gbc_lblRepeatPassword			= new GridBagConstraints(); | ||||
| 		gbc_lblRepeatPassword.anchor	= GridBagConstraints.EAST; | ||||
| 		gbc_lblRepeatPassword.insets	= new Insets(0, 0, 0, 5); | ||||
| 		gbc_lblRepeatPassword.gridx		= 0; | ||||
| 		gbc_lblRepeatPassword.gridy		= 2; | ||||
|  | ||||
| 		repeatPasswordField				= new JPasswordField(); | ||||
| 		gbc_repeatPasswordField			= new GridBagConstraints(); | ||||
| 		gbc_repeatPasswordField.fill	= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_repeatPasswordField.gridx	= 1; | ||||
| 		gbc_repeatPasswordField.gridy	= 2; | ||||
|  | ||||
| 		errorMessage			= new JLabel(); | ||||
| 		gbc_errorMessage		= new GridBagConstraints(); | ||||
| 		gbc_errorMessage.gridx	= 1; | ||||
| 		gbc_errorMessage.gridy	= 3; | ||||
| 		gbc_errorMessage.fill	= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_errorMessage.insets	= new Insets(5, 5, 5, 5); | ||||
| 		errorMessage.setForeground(Color.RED); | ||||
| 		errorMessage.setVisible(false); | ||||
| 		contentPanel.add(errorMessage, gbc_errorMessage); | ||||
|  | ||||
| 		buttonPane = new JPanel(); | ||||
|  | ||||
| 		registerText = new JTextPane(); | ||||
| 		registerText.setEditable(false); | ||||
| 		registerText.setText("Register?"); | ||||
| 		registerText.setFont(new Font("Arial", Font.BOLD, 12)); | ||||
| 		registerText.setAlignmentX(LEFT_ALIGNMENT); | ||||
| 		buttonPane.add(registerText); | ||||
|  | ||||
| 		registerCheckBox = new JCheckBox(); | ||||
| 		registerCheckBox.setAlignmentX(LEFT_ALIGNMENT); | ||||
| 		registerCheckBox.addItemListener(e -> { | ||||
| 			switch (e.getStateChange()) { | ||||
| 				case ItemEvent.SELECTED: | ||||
| 					contentPanel.add(lblRepeatPassword, gbc_lblRepeatPassword); | ||||
| 					contentPanel.add(repeatPasswordField, gbc_repeatPasswordField); | ||||
| 					setSize(338, 173); | ||||
| 					break; | ||||
|  | ||||
| 				case ItemEvent.DESELECTED: | ||||
| 					if (repeatPasswordField.getParent() == contentPanel) { | ||||
| 						contentPanel.remove(lblRepeatPassword); | ||||
| 						contentPanel.remove(repeatPasswordField); | ||||
| 						setSize(338, 148); | ||||
| 					} | ||||
| 					break; | ||||
| 			} | ||||
| 			contentPanel.revalidate(); | ||||
| 			contentPanel.repaint(); | ||||
| 		}); | ||||
| 		buttonPane.add(registerCheckBox); | ||||
|  | ||||
| 		buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); | ||||
| 		getContentPane().add(buttonPane, BorderLayout.SOUTH); | ||||
| 		okButton = new PrimaryButton("OK"); | ||||
| 		okButton.setActionCommand("OK"); | ||||
| 		buttonPane.add(okButton); | ||||
| 		getRootPane().setDefaultButton(okButton); | ||||
|  | ||||
| 		cancelButton = new PrimaryButton("Cancel"); | ||||
| 		cancelButton.setActionCommand("Cancel"); | ||||
| 		buttonPane.add(cancelButton); | ||||
| 		setTheme(); | ||||
|  | ||||
| 		setModalityType(Dialog.DEFAULT_MODALITY_TYPE); | ||||
|  | ||||
| 		EventBus.getInstance().register(HandshakeSuccessfulEvent.class, evt -> dispose()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Resets the text stored in the password fields. | ||||
| 	 * | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	private void clearPasswordFields() { | ||||
| 		passwordField.setText(null); | ||||
| 		repeatPasswordField.setText(null); | ||||
| 	} | ||||
|  | ||||
| 	private void setTheme() { | ||||
| 		Theme theme = Settings.getInstance().getCurrentTheme(); | ||||
|  | ||||
| 		// Panels | ||||
| 		contentPanel.setBackground(theme.getBackgroundColor()); | ||||
| 		contentPanel.setForeground(theme.getBackgroundColor()); | ||||
|  | ||||
| 		buttonPane.setBackground(theme.getBackgroundColor()); | ||||
| 		buttonPane.setForeground(theme.getBackgroundColor()); | ||||
|  | ||||
| 		// Input Fields | ||||
| 		textField.setBackground(theme.getCellColor()); | ||||
| 		textField.setForeground(theme.getUserNameColor()); | ||||
|  | ||||
| 		passwordField.setBackground(theme.getCellColor()); | ||||
| 		passwordField.setForeground(theme.getUserNameColor()); | ||||
|  | ||||
| 		repeatPasswordField.setBackground(theme.getCellColor()); | ||||
| 		repeatPasswordField.setForeground(theme.getUserNameColor()); | ||||
|  | ||||
| 		// JLabels | ||||
| 		lblUserName.setBackground(theme.getCellColor()); | ||||
| 		lblUserName.setForeground(theme.getUserNameColor()); | ||||
|  | ||||
| 		lblPassword.setBackground(theme.getCellColor()); | ||||
| 		lblPassword.setForeground(theme.getUserNameColor()); | ||||
|  | ||||
| 		lblRepeatPassword.setBackground(theme.getCellColor()); | ||||
| 		lblRepeatPassword.setForeground(theme.getUserNameColor()); | ||||
|  | ||||
| 		// Register | ||||
| 		registerText.setBackground(theme.getCellColor()); | ||||
| 		registerText.setForeground(theme.getUserNameColor()); | ||||
|  | ||||
| 		registerCheckBox.setBackground(theme.getCellColor()); | ||||
|  | ||||
| 		// Buttons | ||||
| 		okButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		okButton.setForeground(theme.getInteractableForegroundColor()); | ||||
|  | ||||
| 		cancelButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		cancelButton.setForeground(theme.getInteractableForegroundColor()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Shuts the system down properly if the login was aborted. | ||||
| 	 * | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private void abortLogin() { | ||||
| 		logger.info("The login process has been cancelled. Exiting..."); | ||||
| 		System.exit(0); | ||||
| 	} | ||||
| } | ||||
| @@ -1,13 +0,0 @@ | ||||
| /** | ||||
|  * This package contains all graphical Containers, like Dialogs and Frames.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>package-info.java</strong><br> | ||||
|  * Created: <strong>16 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| package envoy.client.ui.container; | ||||
		Reference in New Issue
	
	Block a user
	 GitHub
						GitHub