2020-03-26 20:23:25 +01:00
|
|
|
package envoy.client.ui;
|
|
|
|
|
2020-09-16 15:41:00 +02:00
|
|
|
import java.io.*;
|
2020-09-01 20:14:02 +02:00
|
|
|
import java.time.Instant;
|
|
|
|
import java.util.concurrent.TimeoutException;
|
2020-09-16 15:41:00 +02:00
|
|
|
import java.util.logging.*;
|
2020-03-27 21:14:49 +01:00
|
|
|
|
2020-03-26 20:23:25 +01:00
|
|
|
import javafx.application.Application;
|
2020-04-10 16:18:01 +02:00
|
|
|
import javafx.scene.control.Alert;
|
|
|
|
import javafx.scene.control.Alert.AlertType;
|
2020-03-26 20:23:25 +01:00
|
|
|
import javafx.stage.Stage;
|
|
|
|
|
2020-04-10 21:05:08 +02:00
|
|
|
import envoy.client.data.*;
|
2020-10-12 16:12:23 +02:00
|
|
|
import envoy.client.data.shortcuts.EnvoyShortcutConfig;
|
2020-09-23 17:03:32 +02:00
|
|
|
import envoy.client.helper.ShutdownHelper;
|
2020-04-10 21:05:08 +02:00
|
|
|
import envoy.client.net.Client;
|
2020-06-08 11:58:57 +02:00
|
|
|
import envoy.client.ui.SceneContext.SceneInfo;
|
|
|
|
import envoy.client.ui.controller.LoginScene;
|
2020-09-25 14:29:23 +02:00
|
|
|
import envoy.client.util.IconUtil;
|
2020-09-01 20:14:02 +02:00
|
|
|
import envoy.data.*;
|
|
|
|
import envoy.data.User.UserStatus;
|
2020-09-16 15:41:00 +02:00
|
|
|
import envoy.event.*;
|
2020-09-01 20:14:02 +02:00
|
|
|
import envoy.exception.EnvoyException;
|
2020-04-10 21:05:08 +02:00
|
|
|
import envoy.util.EnvoyLog;
|
|
|
|
|
2020-03-26 20:23:25 +01:00
|
|
|
/**
|
2020-09-23 17:03:32 +02:00
|
|
|
* Handles application startup.
|
2020-04-02 22:03:43 +02:00
|
|
|
*
|
2020-03-26 20:23:25 +01:00
|
|
|
* @author Kai S. K. Engelbart
|
2020-06-10 22:50:09 +02:00
|
|
|
* @author Maximilian Käfer
|
2020-03-26 20:23:25 +01:00
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
public final class Startup extends Application {
|
|
|
|
|
2020-06-20 10:00:38 +02:00
|
|
|
/**
|
|
|
|
* The version of this client. Used to verify compatibility with the server.
|
2020-06-26 17:33:32 +02:00
|
|
|
*
|
2020-06-20 10:00:38 +02:00
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-09-27 17:39:28 +02:00
|
|
|
public static final String VERSION = "0.2-beta";
|
2020-06-20 10:00:38 +02:00
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
private static LocalDB localDB;
|
2020-03-27 21:14:49 +01:00
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
private static final Context context = Context.getInstance();
|
|
|
|
private static final Client client = context.getClient();
|
2020-06-06 18:30:09 +02:00
|
|
|
private static final ClientConfig config = ClientConfig.getInstance();
|
|
|
|
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
|
2020-03-27 21:14:49 +01:00
|
|
|
|
|
|
|
/**
|
2020-06-09 11:31:22 +02:00
|
|
|
* Loads the configuration, initializes the client and the local database and
|
|
|
|
* delegates the rest of the startup process to {@link LoginScene}.
|
2020-06-13 22:36:52 +02:00
|
|
|
*
|
2020-06-09 11:31:22 +02:00
|
|
|
* @since Envoy Client v0.1-beta
|
2020-03-27 21:14:49 +01:00
|
|
|
*/
|
|
|
|
@Override
|
2020-04-10 16:18:01 +02:00
|
|
|
public void start(Stage stage) throws Exception {
|
2020-09-23 23:11:32 +02:00
|
|
|
|
|
|
|
// Initialize config and logger
|
2020-03-28 15:32:24 +01:00
|
|
|
try {
|
2020-08-22 13:15:42 +02:00
|
|
|
config.loadAll(Startup.class, "client.properties", getParameters().getRaw().toArray(new String[0]));
|
|
|
|
EnvoyLog.initialize(config);
|
|
|
|
} catch (final IllegalStateException e) {
|
2020-04-10 16:18:01 +02:00
|
|
|
new Alert(AlertType.ERROR, "Error loading configuration values:\n" + e);
|
2020-06-14 16:03:02 +02:00
|
|
|
logger.log(Level.SEVERE, "Error loading configuration values: ", e);
|
2020-03-28 15:32:24 +01:00
|
|
|
System.exit(1);
|
|
|
|
}
|
2020-06-19 16:58:37 +02:00
|
|
|
logger.log(Level.INFO, "Envoy starting...");
|
2020-06-14 16:11:46 +02:00
|
|
|
|
2020-03-28 15:32:24 +01:00
|
|
|
// Initialize the local database
|
2020-09-16 15:41:00 +02:00
|
|
|
try {
|
2020-09-23 23:11:32 +02:00
|
|
|
final var localDBFile = new File(config.getHomeDirectory(), config.getServer());
|
2020-09-23 16:23:42 +02:00
|
|
|
logger.info("Initializing LocalDB at " + localDBFile);
|
|
|
|
localDB = new LocalDB(localDBFile);
|
2020-09-19 15:28:04 +02:00
|
|
|
} catch (IOException | EnvoyException e) {
|
|
|
|
logger.log(Level.SEVERE, "Could not initialize local database: ", e);
|
|
|
|
new Alert(AlertType.ERROR, "Could not initialize local database!\n" + e).showAndWait();
|
2020-06-13 22:36:52 +02:00
|
|
|
System.exit(1);
|
|
|
|
return;
|
2020-03-28 15:32:24 +01:00
|
|
|
}
|
2020-09-02 10:37:26 +02:00
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
// Prepare handshake
|
|
|
|
context.setLocalDB(localDB);
|
2020-03-28 15:32:24 +01:00
|
|
|
|
2020-09-18 10:01:57 +02:00
|
|
|
// Configure stage
|
2020-09-01 20:14:02 +02:00
|
|
|
stage.setTitle("Envoy");
|
|
|
|
stage.getIcons().add(IconUtil.loadIcon("envoy_logo"));
|
|
|
|
|
2020-10-12 16:12:23 +02:00
|
|
|
// Configure global shortcuts
|
|
|
|
EnvoyShortcutConfig.initializeEnvoyShortcuts();
|
|
|
|
|
2020-09-18 10:01:57 +02:00
|
|
|
// Create scene context
|
2020-09-01 20:14:02 +02:00
|
|
|
final var sceneContext = new SceneContext(stage);
|
|
|
|
context.setSceneContext(sceneContext);
|
|
|
|
|
2020-09-19 11:37:42 +02:00
|
|
|
// Authenticate with token if present
|
|
|
|
if (localDB.getAuthToken() != null) {
|
|
|
|
logger.info("Attempting authentication with token...");
|
|
|
|
localDB.loadUserData();
|
2020-09-19 13:33:18 +02:00
|
|
|
if (!performHandshake(
|
|
|
|
LoginCredentials.loginWithToken(localDB.getUser().getName(), localDB.getAuthToken(), VERSION, localDB.getLastSync())))
|
|
|
|
sceneContext.load(SceneInfo.LOGIN_SCENE);
|
2020-09-22 14:42:51 +02:00
|
|
|
} else
|
2020-09-19 11:37:42 +02:00
|
|
|
// Load login scene
|
|
|
|
sceneContext.load(SceneInfo.LOGIN_SCENE);
|
2020-09-01 20:14:02 +02:00
|
|
|
}
|
2020-07-09 09:12:41 +02:00
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
/**
|
|
|
|
* Tries to perform a Handshake with the server.
|
|
|
|
*
|
|
|
|
* @param credentials the credentials to use for the handshake
|
2020-09-19 13:33:18 +02:00
|
|
|
* @return whether the handshake was successful or offline mode could be entered
|
2020-09-01 20:14:02 +02:00
|
|
|
* @since Envoy Client v0.2-beta
|
|
|
|
*/
|
2020-09-19 13:33:18 +02:00
|
|
|
public static boolean performHandshake(LoginCredentials credentials) {
|
2020-07-09 09:37:31 +02:00
|
|
|
final var cacheMap = new CacheMap();
|
2020-07-09 09:12:41 +02:00
|
|
|
cacheMap.put(Message.class, new Cache<Message>());
|
|
|
|
cacheMap.put(GroupMessage.class, new Cache<GroupMessage>());
|
|
|
|
cacheMap.put(MessageStatusChange.class, new Cache<MessageStatusChange>());
|
|
|
|
cacheMap.put(GroupMessageStatusChange.class, new Cache<GroupMessageStatusChange>());
|
2020-10-12 16:12:23 +02:00
|
|
|
final var originalStatus = localDB.getUser() == null ? UserStatus.ONLINE : localDB.getUser().getStatus();
|
2020-09-01 20:14:02 +02:00
|
|
|
try {
|
|
|
|
client.performHandshake(credentials, cacheMap);
|
|
|
|
if (client.isOnline()) {
|
2020-10-09 18:23:00 +02:00
|
|
|
|
|
|
|
// Restore the original status as the server automatically returns status ONLINE
|
|
|
|
client.getSender().setStatus(originalStatus);
|
2020-09-01 20:14:02 +02:00
|
|
|
loadChatScene();
|
|
|
|
client.initReceiver(localDB, cacheMap);
|
2020-09-19 13:33:18 +02:00
|
|
|
return true;
|
2020-09-20 22:11:15 +02:00
|
|
|
} else return false;
|
2020-09-01 20:14:02 +02:00
|
|
|
} catch (IOException | InterruptedException | TimeoutException e) {
|
|
|
|
logger.log(Level.INFO, "Could not connect to server. Entering offline mode...");
|
2020-09-19 13:33:18 +02:00
|
|
|
return attemptOfflineMode(credentials.getIdentifier());
|
2020-09-01 20:14:02 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-28 15:32:24 +01:00
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
/**
|
|
|
|
* Attempts to load {@link envoy.client.ui.controller.ChatScene} in offline mode
|
|
|
|
* for a given user.
|
|
|
|
*
|
|
|
|
* @param identifier the identifier of the user - currently his username
|
2020-09-19 13:33:18 +02:00
|
|
|
* @return whether the offline mode could be entered
|
2020-09-01 20:14:02 +02:00
|
|
|
* @since Envoy Client v0.2-beta
|
|
|
|
*/
|
2020-09-19 13:33:18 +02:00
|
|
|
public static boolean attemptOfflineMode(String identifier) {
|
2020-09-01 20:14:02 +02:00
|
|
|
try {
|
|
|
|
// Try entering offline mode
|
|
|
|
final User clientUser = localDB.getUsers().get(identifier);
|
|
|
|
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
|
|
|
|
client.setSender(clientUser);
|
|
|
|
loadChatScene();
|
2020-09-19 13:33:18 +02:00
|
|
|
return true;
|
2020-09-01 20:14:02 +02:00
|
|
|
} catch (final Exception e) {
|
|
|
|
new Alert(AlertType.ERROR, "Client error: " + e).showAndWait();
|
|
|
|
logger.log(Level.SEVERE, "Offline mode could not be loaded: ", e);
|
|
|
|
System.exit(1);
|
2020-09-19 13:33:18 +02:00
|
|
|
return false;
|
2020-09-01 20:14:02 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-28 10:39:15 +01:00
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
/**
|
|
|
|
* Loads the last known time a user has been online.
|
|
|
|
*
|
|
|
|
* @param identifier the identifier of this user - currently his name
|
|
|
|
* @return the last {@code Instant} at which he has been online
|
|
|
|
* @since Envoy Client v0.2-beta
|
|
|
|
*/
|
|
|
|
public static Instant loadLastSync(String identifier) {
|
|
|
|
try {
|
|
|
|
localDB.setUser(localDB.getUsers().get(identifier));
|
|
|
|
localDB.loadUserData();
|
|
|
|
} catch (final Exception e) {
|
|
|
|
// User storage empty, wrong user name etc. -> default lastSync
|
|
|
|
}
|
|
|
|
return localDB.getLastSync();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void loadChatScene() {
|
|
|
|
|
|
|
|
// Set client user in local database
|
2020-10-09 18:23:00 +02:00
|
|
|
final var user = client.getSender();
|
|
|
|
localDB.setUser(user);
|
2020-09-01 20:14:02 +02:00
|
|
|
|
|
|
|
// Initialize chats in local database
|
|
|
|
try {
|
|
|
|
localDB.loadUserData();
|
|
|
|
} catch (final FileNotFoundException e) {
|
|
|
|
// The local database file has not yet been created, probably first login
|
|
|
|
} catch (final Exception e) {
|
|
|
|
new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
|
|
|
|
logger.log(Level.WARNING, "Could not load local database: ", e);
|
|
|
|
}
|
|
|
|
|
2020-09-02 10:07:02 +02:00
|
|
|
context.initWriteProxy();
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-10-09 18:23:00 +02:00
|
|
|
if (client.isOnline()) {
|
|
|
|
context.getWriteProxy().flushCache();
|
|
|
|
|
|
|
|
// Inform the server that this user has a different user status than expected
|
|
|
|
if (!user.getStatus().equals(UserStatus.ONLINE)) client.send(new UserStatusChange(user));
|
|
|
|
} else
|
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
// Set all contacts to offline mode
|
|
|
|
localDB.getChats()
|
|
|
|
.stream()
|
|
|
|
.map(Chat::getRecipient)
|
|
|
|
.filter(User.class::isInstance)
|
|
|
|
.map(User.class::cast)
|
|
|
|
.forEach(u -> u.setStatus(UserStatus.OFFLINE));
|
|
|
|
|
|
|
|
final var stage = context.getStage();
|
2020-09-02 10:07:02 +02:00
|
|
|
|
|
|
|
// Pop LoginScene if present
|
|
|
|
if (!context.getSceneContext().isEmpty()) context.getSceneContext().pop();
|
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
// Load ChatScene
|
|
|
|
stage.setMinHeight(400);
|
|
|
|
stage.setMinWidth(843);
|
|
|
|
context.getSceneContext().load(SceneContext.SceneInfo.CHAT_SCENE);
|
|
|
|
stage.centerOnScreen();
|
|
|
|
|
2020-09-28 15:58:42 +02:00
|
|
|
// Exit or minimize the stage when a close request occurs
|
|
|
|
stage.setOnCloseRequest(
|
|
|
|
e -> { ShutdownHelper.exit(); if (Settings.getInstance().isHideOnClose() && StatusTrayIcon.isSupported()) e.consume(); });
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-09-28 15:58:42 +02:00
|
|
|
if (StatusTrayIcon.isSupported()) {
|
2020-09-01 20:14:02 +02:00
|
|
|
|
|
|
|
// Initialize status tray icon
|
|
|
|
final var trayIcon = new StatusTrayIcon(stage);
|
|
|
|
Settings.getInstance().getItems().get("hideOnClose").setChangeHandler(c -> {
|
|
|
|
if ((Boolean) c) trayIcon.show();
|
|
|
|
else trayIcon.hide();
|
|
|
|
});
|
|
|
|
}
|
2020-09-22 16:37:43 +02:00
|
|
|
|
|
|
|
// Start auto save thread
|
|
|
|
localDB.initAutoSave();
|
2020-03-27 21:14:49 +01:00
|
|
|
}
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|