Version 1.0 - entire UI is in cz, added config manager and help dialog, fixed bugs

This commit is contained in:
Thastertyn 2023-05-26 22:09:04 +02:00
parent 5f817f3dfc
commit ae043ccb2f
19 changed files with 696 additions and 275 deletions

44
.gitignore vendored
View File

@ -1,2 +1,44 @@
# ---> Java
# Compiled class file
*.class
jecnak-tui.jar
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
# ---> Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
# Eclipse m2e generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath

View File

@ -6,11 +6,11 @@
<groupId>xyz.thastertyn</groupId>
<artifactId>jecnak-tui</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<name>jecnak-tui</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<url>https://gitea.thastertyn.xyz/Thastertyn/jecnak-tui</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@ -4,13 +4,13 @@ import xyz.thastertyn.UserInterface.MainWindow;
/**
* Hello world!
*
*
*/
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
MainWindow window = new MainWindow();
window.run();
}
public static void main(String[] args) {
System.out.println("Hello World!");
MainWindow window = new MainWindow();
window.run();
}
}

View File

@ -0,0 +1,273 @@
package xyz.thastertyn.Config;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.naming.ConfigurationException;
import xyz.thastertyn.Types.Credentials;
/**
* Config parser for key value pairs with support for # comments
*/
public class Config {
private String path;
private File configFile;
private File configPath;
private boolean configExists = false;
private boolean isDataLoaded = false;
private boolean hasReadFailed = false;
private boolean hasWriteFailed = false;
private HashMap<String, String> configData = new HashMap<>();
private static Config config = new Config();
//Tries finding appropriate path to the file
public Config()
{
if(System.getProperty("os.name").equals("Linux"))
{
// /home/user/.local/share/jecnak/...
path = System.getProperty("user.home") + "/.config/jecnak/";
}else if(System.getProperty("os.name").contains("Windows"))
{
// C:\Users\\user\AppData\Roaming\...
path = System.getenv("APPDATA") + "\\jecnak\\";
}else{
configExists = false;
}
configPath = new File(path);
configFile = new File(configPath, "config.conf");
if(!configFile.exists())
{
createConfigFile();
}
configExists = true;
try{
readConfig();
}catch(ConfigurationException e)
{
hasReadFailed = true;
}
}
/**
* When no file exists, it is created together with it's directory
* @return true or false whether the action succeeded. {@link #hasWriteFailed} is set to true if false is returned
*/
private boolean createConfigFile()
{
try{
configPath.mkdirs();
configFile.createNewFile();
return true;
}catch(IOException e)
{
hasWriteFailed = true;
return false;
}
}
/**
* Tries reading the config file and parse it into {@link #configData}
* @throws ConfigurationException when a syntax error in the config is found, i.e. "user=" missing the value or just random gibberish
*/
private void readConfig() throws ConfigurationException
{
try {
BufferedReader reader = new BufferedReader(new FileReader(configFile));
String line = "";
int lineNum = 1;
while((line = reader.readLine()) != null)
{
if(line.matches("^\\s*#.*"))
{
configData.put("comment" + lineNum, line);
}else{
String[] data = line.split("=");
// Check if key or value are null or emtpy and if yes, call that a syntax error
if((data[0] == null || data[0].trim().isBlank()) || (data[1] == null ||data[1].trim().isBlank()))
{
reader.close();
hasReadFailed = true;
throw new ConfigurationException("Syntax Error on line " + lineNum + "\n" + "-> " + line);
}else{
configData.put(data[0], data[1]);
}
}
lineNum++;
}
isDataLoaded = true;
reader.close();
} catch (IOException e) {
hasReadFailed = true;
e.printStackTrace();
}
}
/**
* Checks if a key is stored in {@link #configData}
* @param key String key
* @return true or false if it exists or not
*/
public boolean checkIfExists(String key)
{
return configData.containsKey(key);
}
/**
* Shortcut for checking credentials specifically. Just runs {@link #checkIfExists(String)} for user and pass keys
* @return true or false if they exist or not
*/
public boolean checkIfCredentialsExist()
{
return (checkIfExists("user") && checkIfExists("pass"));
}
/**
* Tries getting the credentials, should be combined with {@link #checkIfCredentialsExist()} to assure no null values are returned
* @return {@link Credentials} containing the username and password
* @throws ConfigurationException when {@link #readConfig()} fails to read the config file, meaning nothing is loaded at all. Basically force the check if nothing is returned.
*/
public Credentials getCredentials() throws ConfigurationException
{
return new Credentials(getConfig("user"), getConfig("pass"));
}
/**
* Gets a specific key from {@link #configData}
* @param key to get
* @return value assigned to the key
* @throws ConfigurationException when {@link #readConfig()} fails to read to force addressing the possibility of no data
*/
public String getConfig(String key) throws ConfigurationException
{
if(!configExists)
{
return "";
}
if(!isDataLoaded)
{
readConfig();
}
if(!hasReadFailed)
{
String value = configData.get(key);
return (value == null) ? "" : value;
}else{
return "";
}
}
/**
* Adds a key and value to {@link #configData}
* @param key
* @param value
* @return {@code this} for the ability to chain these methods
*/
public Config addConfig(String key, String value)
{
configData.put(key, value);
return this;
}
/**
* Removes a key from {@link #configData}
* @param key
* @return {@code this} for the ability to chain these methods
*/
public Config removeConfig(String key)
{
configData.remove(key);
return this;
}
/**
* Removes credentials from {@link #configData}
* @return {@code this} for the ability to chain these methods
*/
public Config removeCredentials()
{
configData.remove("user");
configData.remove("pass");
return this;
}
/**
* Tries writting current state of {@link #configData} to {@link #configFile}
* @return true or false whether it succeeded or not
*/
public boolean writeConfig()
{
if(hasWriteFailed)
{
return false;
}
if(!configFile.exists())
{
createConfigFile();
}
StringBuilder finalString = new StringBuilder();
for(Map.Entry<String, String> entry : configData.entrySet())
{
if(entry.getKey().matches("comment[0-9]+"))
{
finalString.append(entry.getValue() + "\n");
}else{
finalString.append(entry.getKey() + "=" + entry.getValue() + "\n");
}
}
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(configFile));
writer.write(finalString.toString());
writer.close();
return true;
} catch (IOException e) {
e.printStackTrace();
hasWriteFailed = true;
return false;
}
}
public String getPath()
{
return configFile.getAbsolutePath();
}
/**
* Singleton
* @return {@link #config}
*/
public static Config getInstance()
{
return config;
}
}

View File

@ -1,6 +1,7 @@
package xyz.thastertyn.Login;
import java.util.Arrays;
import java.util.Optional;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.gui2.Button;
@ -30,21 +31,20 @@ public class CredentialsInput extends DialogWindow {
private String user;
private String pass;
private boolean wasCanceled = false;
private CheckBox remember;
public CredentialsInput()
{
super("Login");
super("Prihlaseni");
this.user = null;
this.pass = null;
this.username = new TextBox();
this.password = new TextBox().setMask('*');
this.remember = new CheckBox();
Panel buttonPanel = new Panel();
buttonPanel
.setLayoutManager(
new GridLayout(2).setHorizontalSpacing(1))
Panel buttonPanel = new Panel(new GridLayout(2).setHorizontalSpacing(1))
.addComponent(
new Button(LocalizedString.OK.toString(), this::onOK)
.setLayoutData(GridLayout.createLayoutData(
@ -55,17 +55,15 @@ public class CredentialsInput extends DialogWindow {
.addComponent(
new Button(LocalizedString.Cancel.toString(), this::onCancel));
Panel mainPanel = new Panel()
.setLayoutManager(new GridLayout(1)
Panel mainPanel = new Panel(new GridLayout(1)
.setLeftMarginSize(1)
.setRightMarginSize(1));
mainPanel.addComponent(new Label("Enter your username and password"));
mainPanel.addComponent(new Label("Zadej sve jmeno a heslo"));
mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
Panel userPanel = new Panel()
.setLayoutManager(new GridLayout(3))
Panel userPanel = new Panel(new GridLayout(3))
.setLayoutData(
GridLayout.createLayoutData(
GridLayout.Alignment.FILL,
@ -80,7 +78,7 @@ public class CredentialsInput extends DialogWindow {
true,
false));
userPanel.addComponent(new Label("Username: "))
userPanel.addComponent(new Label("Jmeno: "))
.addComponent(username)
.addTo(mainPanel);
@ -92,29 +90,27 @@ public class CredentialsInput extends DialogWindow {
false))
.addTo(mainPanel);
Panel passPanel = new Panel()
.setLayoutManager(new GridLayout(3))
Panel passPanel = new Panel(new GridLayout(3))
.setLayoutData(GridLayout.createLayoutData(
GridLayout.Alignment.FILL,
Alignment.CENTER,
true,
false));
passPanel.addComponent(new Label("Password: "))
passPanel.addComponent(new Label("Heslo: "))
.addComponent(password)
.addTo(mainPanel);
if(!LocalCredentials.getInstance().checkForExistingCredentials())
if(!xyz.thastertyn.Config.Config.getInstance().checkIfCredentialsExist())
{
Panel rememberPanel = new Panel()
.setLayoutManager(new GridLayout(3))
Panel rememberPanel = new Panel(new GridLayout(3))
.setLayoutData(GridLayout.createLayoutData(
GridLayout.Alignment.FILL,
Alignment.CENTER,
true,
false));
rememberPanel.addComponent(new Label("Rembember?"))
rememberPanel.addComponent(new Label("Zapamatovat?"))
.addComponent(remember)
.addTo(mainPanel);
}
@ -142,7 +138,7 @@ public class CredentialsInput extends DialogWindow {
if (user.isEmpty() || pass.isEmpty())
{
MessageDialog.showMessageDialog(getTextGUI(), getTitle(), "Username and password cannot be blank",
MessageDialog.showMessageDialog(getTextGUI(), getTitle(), "Uzivatelske jmeno a heslo nemohou byt prazdne",
MessageDialogButton.OK);
return;
}
@ -152,6 +148,7 @@ public class CredentialsInput extends DialogWindow {
public void onCancel() {
close();
wasCanceled = true;
}
/**
@ -159,8 +156,14 @@ public class CredentialsInput extends DialogWindow {
* @return {@link InputtedCredentials} with username first, password second and whether to store the credentials third
*/
@Override
public InputtedCredentials showDialog(WindowBasedTextGUI textGUI) {
public Optional<InputtedCredentials> showDialog(WindowBasedTextGUI textGUI) {
super.showDialog(textGUI);
return new InputtedCredentials(user, pass, remember.isChecked());
if(wasCanceled)
{
return Optional.empty();
}
return Optional.of(new InputtedCredentials(user, pass, remember.isChecked()));
}
}

View File

@ -1,162 +0,0 @@
package xyz.thastertyn.Login;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import xyz.thastertyn.Types.Credentials;
public class LocalCredentials {
private static LocalCredentials localCredentials = new LocalCredentials();
private String path;
private File credentialsFile;
private File credentialsPath;
/**
* Checks for already saved credentials, at least if its file exists <br>
* for Windows it checks {@code AppData\Roaming\jecnak\credemtials.txt} <br>
* for Linux it checks {@code ~/.local/share/jecnak/credentials.txt} <br>
* @return
* <ul>
* <li> {@code true} a file exists </li>
* <li> {@code false} doesn't exist </li>
* </ul>
*/
public boolean checkForExistingCredentials()
{
if(System.getProperty("os.name").equals("Linux"))
{
// /home/user/.local/share/jecnak/...
path = System.getProperty("user.home") + "/.local/share/jecnak/";
}else if(System.getProperty("os.name").contains("Windows"))
{
// C:\Users\\user\AppData\Roaming\...
path = System.getenv("APPDATA") + "\\jecnak\\";
}else{
return false;
}
credentialsPath = new File(path);
credentialsFile = new File(credentialsPath, "credentials.txt");
if(!credentialsFile.exists())
{
return false;
}
return true;
}
/**
* Try reading the credentails from a local file
* @return String[] with username on index 0, and password on index 1, <br>
* can also return null if credentials are corrupted, inaccessible, etc.
*/
public Credentials getCredentialsFile()
{
// TODO Use hashmap instead of array
String user = null;
String pass = null;
if(credentialsFile == null)
{
return null;
}
try {
BufferedReader reader = new BufferedReader(new FileReader(credentialsFile));
String line = "";
while((line = reader.readLine()) != null)
{
String[] currentValue = line.split("=");
if(currentValue[0].equals("user"))
{
user = currentValue[1];
}else if(currentValue[0].equals("pass"))
{
pass = currentValue[1];
}
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
return null;
}
if(user == null || pass == null)
{
return null;
}
return new Credentials(user, pass);
}
/**
* appends the current credentials to the file
* @param credentials the array containing username and password. Username is on index 0, password on index 1
* @return
*/
public boolean saveCredentials(Credentials credentials)
{
if(credentialsFile == null)
{
return false;
}
if(!credentialsFile.exists())
{
try{
credentialsPath.mkdirs();
credentialsFile.createNewFile();
}catch(IOException e)
{
return false;
}
}
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(credentialsFile));
writer.append(String.format(
"user=%s\npass=%s\n",
credentials.getUsername(),
credentials.getPassword()));
writer.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Deletes the credentials file
* @return
*/
public boolean deleteCredentials()
{
credentialsFile.delete();
return true;
}
/**
* Returns a single instance to prevent creating and loading the files all over again when not much has changed
* @return {@link LocalCredentials} instance
*/
public static LocalCredentials getInstance()
{
return localCredentials;
}
}

View File

@ -2,25 +2,29 @@ package xyz.thastertyn.Login;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Optional;
import java.util.concurrent.TimeoutException;
import javax.naming.ConfigurationException;
import javax.security.auth.login.CredentialException;
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import com.googlecode.lanterna.gui2.dialogs.MessageDialog;
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton;
import xyz.thastertyn.Config.Config;
import xyz.thastertyn.Scrape.Downloader;
import xyz.thastertyn.Types.Credentials;
import xyz.thastertyn.Types.InputtedCredentials;
/**
* Merges the functionality of {@link LocalCredentials}, {@link CredentialsInput}, and {@link Login}
* Merges the functionality of {@link Config}, {@link CredentialsInput}, and {@link Login}
*/
public class LoginController {
private WindowBasedTextGUI textGUI;
private xyz.thastertyn.Login.CredentialsInput dialog;
private LocalCredentials localCredentials = LocalCredentials.getInstance();
private Config config = Config.getInstance();
private xyz.thastertyn.Login.Login login = new xyz.thastertyn.Login.Login();
public LoginController(WindowBasedTextGUI textGUI)
@ -37,30 +41,60 @@ public class LoginController {
*/
public void login(boolean forceGUI)
{
Credentials credentials;
if(!forceGUI && localCredentials.checkForExistingCredentials())
if(forceGUI || !config.checkIfCredentialsExist())
{
credentials = localCredentials.getCredentialsFile();
Optional<Credentials> optional = loginUsingGui();
if(optional.isEmpty())
{
if(!Downloader.isInitialized())
{
System.exit(0);
}
return;
}
useCredentials(optional.get());
}else{
credentials = loginUsingGui();
try{
useCredentials(config.getCredentials());
}catch(ConfigurationException e) {
e.printStackTrace();
login(true);
}
}
useCredentials(credentials);
}
public Credentials loginUsingGui()
public Optional<Credentials> loginUsingGui()
{
dialog = new CredentialsInput();
InputtedCredentials credentials = dialog.showDialog(textGUI);
Optional<InputtedCredentials> credentialsOptional = dialog.showDialog(textGUI);
if(credentials.save())
if(credentialsOptional.isEmpty())
{
localCredentials.saveCredentials(credentials.getCredentials());
if(!Downloader.isInitialized())
{
System.exit(0);
}
return Optional.empty();
}
return credentials.getCredentials();
InputtedCredentials inputtedCredentials = credentialsOptional.get();
if(inputtedCredentials.save())
{
config.addConfig("user", inputtedCredentials.getUsername());
config.addConfig("pass", inputtedCredentials.getPassword());
if(!config.writeConfig())
{
MessageDialog.showMessageDialog(textGUI, "Failed to save credentials", "Something has gone wrong while saving your credentials", MessageDialogButton.OK);
}
}
return Optional.of(inputtedCredentials.getCredentials());
}
/**
@ -86,7 +120,9 @@ public class LoginController {
e.getMessage(),
MessageDialogButton.OK);
// The credentials were most likely tampered with after save, just get rid of them and save them another time
LocalCredentials.getInstance().deleteCredentials();
Config.getInstance()
.removeCredentials()
.writeConfig();
login(true);
} catch (IOException e)
{

View File

@ -2,9 +2,7 @@ package xyz.thastertyn.Scrape;
import java.io.IOException;
import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.Arrays;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@ -17,7 +15,6 @@ import xyz.thastertyn.Types.Options;
public class AbsenceList extends JecnaScrape {
private ArrayList<xyz.thastertyn.Types.AbsenceList> data;
private Choice currentChoice;
private Options schoolYearOptions;
@ -30,8 +27,6 @@ public class AbsenceList extends JecnaScrape {
@Override
public void download(Choice choice) throws IOException {
currentChoice = choice;
parse(String.format(
"https://www.spsejecna.cz/absence/student?schoolYearId=%s",
choice.getChoices().get(0)));
@ -64,8 +59,6 @@ public class AbsenceList extends JecnaScrape {
boolean isDefault = e.hasAttr("selected");
schoolYearOptions.addOption(new Option(e.text(), e.attr("value"), isDefault));
}
currentChoice = new Choice(Arrays.asList(schoolYearOptions.getOptions().get(0).getValue()));
}
/**

View File

@ -10,6 +10,8 @@ public class Downloader {
private static String JsessionId;
private static boolean isInitialized = false;
/**
* Provide a general Jsoup Connection for simplicity with some predefined values and JSessionId already set
* @param url
@ -46,5 +48,11 @@ public class Downloader {
public static void setJSessionId(String newJsessionId)
{
JsessionId = newJsessionId;
isInitialized = true;
}
public static boolean isInitialized()
{
return isInitialized;
}
}

View File

@ -17,9 +17,8 @@ public class AbsenceList extends JecnaContent{
public AbsenceList(UpdateListener listener)
{
super(listener);
this.mainPanel = new Panel()
.setLayoutManager(new GridLayout(1));
this.borderLabel = new Label("Omluvny L.");
this.mainPanel = new Panel(new GridLayout(1));
this.borderLabel = new Label("Absence");
super.scraper = omluvnyList;
}
@ -29,8 +28,7 @@ public class AbsenceList extends JecnaContent{
this.mainPanel.removeAllComponents();
ArrayList<xyz.thastertyn.Types.AbsenceList> a = omluvnyList.getData();
Panel content = new Panel()
.setLayoutManager(new LinearLayout(Direction.VERTICAL))
Panel content = new Panel(new LinearLayout(Direction.VERTICAL))
.addTo(mainPanel);
for(xyz.thastertyn.Types.AbsenceList omluvnyList : a)

View File

@ -1,6 +1,7 @@
package xyz.thastertyn.UserInterface.Content;
import java.io.IOException;
import java.util.Optional;
import com.googlecode.lanterna.gui2.Label;
import com.googlecode.lanterna.gui2.Panel;
@ -77,11 +78,11 @@ public abstract class JecnaContent {
OptionsDialog dialog = new OptionsDialog(scraper.getOptions());
Choice choice = dialog.showDialog(textGUI);
Optional<Choice> choice = dialog.showDialog(textGUI);
if(choice != null)
if(choice.isPresent())
{
download(choice);
download(choice.get());
}
}

View File

@ -12,6 +12,7 @@ import com.googlecode.lanterna.gui2.Label;
import com.googlecode.lanterna.gui2.LayoutData;
import com.googlecode.lanterna.gui2.LinearLayout;
import com.googlecode.lanterna.gui2.Panel;
import com.googlecode.lanterna.gui2.Separator;
import xyz.thastertyn.Types.FinalMark;
import xyz.thastertyn.Types.Subject;
@ -26,6 +27,7 @@ public class Marks extends JecnaContent {
private final TextColor.RGB DOSTATECNY = new TextColor.RGB(255,102,0);
private final TextColor.RGB NEDOSTATECNY = new TextColor.RGB(255,48,48);
private final TextColor.RGB NEHODNOCEN = new TextColor.RGB(0,0,0);
private final TextColor.RGB UVOLNEN = new TextColor.RGB(28, 107, 255);
private final LayoutData ALIGN_LEFT = GridLayout.createLayoutData(
GridLayout.Alignment.BEGINNING,
@ -45,8 +47,7 @@ public class Marks extends JecnaContent {
{
super(listener);
this.mainPanel = new Panel()
.setLayoutManager(new GridLayout(3));
this.mainPanel = new Panel(new GridLayout(5));
this.borderLabel = new Label("Znamky");
super.scraper = this.marks;
}
@ -65,31 +66,40 @@ public class Marks extends JecnaContent {
colors.put(3, new SimpleTheme(ANSI.BLACK, DOBRY));
colors.put(4, new SimpleTheme(ANSI.BLACK, DOSTATECNY));
colors.put(5, new SimpleTheme(ANSI.BLACK, NEDOSTATECNY));
colors.put(0, new SimpleTheme(ANSI.BLACK, ANSI.WHITE));
colors.put(-1, new SimpleTheme(ANSI.WHITE, NEHODNOCEN));
colors.put(-2, new SimpleTheme(ANSI.WHITE, NEHODNOCEN));
colors.put(-3, new SimpleTheme(ANSI.WHITE, UVOLNEN));
colors.put(-4, new SimpleTheme(ANSI.BLACK, NEDOSTATECNY));
colors.put(-5, new SimpleTheme(ANSI.BLACK, ANSI.WHITE));
// Column for subject names
Panel subjectNames = new Panel()
.setLayoutManager(new LinearLayout(Direction.VERTICAL))
Panel subjectNames = new Panel(new LinearLayout(Direction.VERTICAL))
.setLayoutData(ALIGN_LEFT)
.addTo(mainPanel);
Panel emptySpaceLeft = new Panel(new LinearLayout(Direction.VERTICAL))
.addTo(mainPanel);
// Column for marks
Panel marks = new Panel()
.setLayoutManager(new LinearLayout(Direction.VERTICAL))
Panel marks = new Panel(new LinearLayout(Direction.VERTICAL))
.setLayoutData(ALIGN_LEFT)
.addTo(mainPanel);
Panel emptySpaceRight = new Panel(new LinearLayout(Direction.VERTICAL))
.setLayoutData(ALIGN_RIGHT)
.addTo(mainPanel);
// Column for final mark
Panel finalMarks = new Panel()
.setLayoutManager(new LinearLayout(Direction.VERTICAL))
Panel finalMarks = new Panel(new LinearLayout(Direction.VERTICAL))
.setLayoutData(ALIGN_RIGHT)
.addTo(mainPanel);
for(Subject subject : subjects)
{
Panel individualMarks = new Panel()
.setLayoutManager(new LinearLayout(Direction.HORIZONTAL))
emptySpaceLeft.addComponent(new Separator(Direction.VERTICAL));
emptySpaceRight.addComponent(new Separator(Direction.VERTICAL));
Panel individualMarks = new Panel(new LinearLayout(Direction.HORIZONTAL))
.addTo(marks);
if(subject.getMarks().isEmpty())

View File

@ -1,5 +1,6 @@
package xyz.thastertyn.UserInterface.Content;
import java.io.IOException;
import java.util.ArrayList;
import com.googlecode.lanterna.TextColor.ANSI;
@ -8,6 +9,9 @@ import com.googlecode.lanterna.gui2.GridLayout;
import com.googlecode.lanterna.gui2.Label;
import com.googlecode.lanterna.gui2.LinearLayout;
import com.googlecode.lanterna.gui2.Panel;
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import com.googlecode.lanterna.gui2.dialogs.MessageDialog;
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton;
import xyz.thastertyn.UserInterface.Listeners.UpdateListener;
@ -18,7 +22,7 @@ public class Reports extends JecnaContent {
public Reports(UpdateListener listener)
{
super(listener);
this.mainPanel = new Panel().setLayoutManager(new GridLayout(1)
this.mainPanel = new Panel(new GridLayout(1)
.setLeftMarginSize(1)
.setRightMarginSize(1));
this.borderLabel = new Label("Sdeleni R.");
@ -34,7 +38,7 @@ public class Reports extends JecnaContent {
for(xyz.thastertyn.Types.Reports reports : reportsList)
{
Panel row = new Panel().setLayoutManager(new LinearLayout(Direction.HORIZONTAL));
Panel row = new Panel(new LinearLayout(Direction.HORIZONTAL));
Label checkmark = new Label("");
Label text = new Label(reports.getText());
@ -53,4 +57,10 @@ public class Reports extends JecnaContent {
.addTo(mainPanel);
}
}
@Override
public void showOptions(WindowBasedTextGUI textGUI) throws IOException {
MessageDialog.showMessageDialog(textGUI, "Zadny vyber", "Zde neni co vybirat", MessageDialogButton.OK);
}
}

View File

@ -11,7 +11,7 @@ import com.googlecode.lanterna.gui2.Window;
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import com.googlecode.lanterna.gui2.dialogs.DialogWindow;
import xyz.thastertyn.Login.LocalCredentials;
import xyz.thastertyn.Config.Config;
import xyz.thastertyn.Login.LoginController;
import xyz.thastertyn.UserInterface.Listeners.ContentResetListener;
@ -26,18 +26,16 @@ public class EscapeDialog extends DialogWindow {
this.textGUI = textGUI;
this.resetListener = resetListener;
Panel mainPanel = new Panel()
.setLayoutManager(new GridLayout(1)
Panel mainPanel = new Panel(
new GridLayout(1)
.setLeftMarginSize(1)
.setRightMarginSize(1));
new Panel()
.setLayoutManager(
new LinearLayout(Direction.VERTICAL))
.addComponent(new Button("Return", this::onReturn))
.addComponent(new Button("Logout", this::onLogout))
.addComponent(new Button("Delete stored login", this::onDeleteLogin))
.addComponent(new Button("Exit", this::onExit))
new Panel(new LinearLayout(Direction.VERTICAL))
.addComponent(new Button("Zpet", this::onReturn))
.addComponent(new Button("Odhlasit", this::onLogout))
.addComponent(new Button("Smazat ulozene udaje", this::onDeleteLogin))
.addComponent(new Button("Ukoncit", this::onExit))
.addTo(mainPanel);
setHints(Arrays.asList(Window.Hint.CENTERED));
@ -52,7 +50,9 @@ public class EscapeDialog extends DialogWindow {
public void onDeleteLogin()
{
close();
LocalCredentials.getInstance().deleteCredentials();
Config.getInstance()
.removeCredentials()
.writeConfig();
LoginController controller = new LoginController(textGUI);
controller.loginUsingGui();
resetListener.reset();
@ -74,7 +74,6 @@ public class EscapeDialog extends DialogWindow {
@Override
public Object showDialog(WindowBasedTextGUI textGUI) {
super.showDialog(textGUI);
return null;
return super.showDialog(textGUI);
}
}

View File

@ -0,0 +1,70 @@
package xyz.thastertyn.UserInterface.Dialogs;
import java.util.Arrays;
import com.googlecode.lanterna.gui2.Button;
import com.googlecode.lanterna.gui2.CheckBox;
import com.googlecode.lanterna.gui2.Direction;
import com.googlecode.lanterna.gui2.EmptySpace;
import com.googlecode.lanterna.gui2.GridLayout;
import com.googlecode.lanterna.gui2.Label;
import com.googlecode.lanterna.gui2.LinearLayout;
import com.googlecode.lanterna.gui2.LocalizedString;
import com.googlecode.lanterna.gui2.Panel;
import com.googlecode.lanterna.gui2.Window;
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import com.googlecode.lanterna.gui2.dialogs.DialogWindow;
public class HelpDialog extends DialogWindow {
CheckBox box = new CheckBox("Znovu nezobrazovat");
public HelpDialog(boolean startupScreen)
{
super("Napoveda");
Panel mainPanel = new Panel(new GridLayout(1)
.setLeftMarginSize(1)
.setRightMarginSize(1));
new Panel(new LinearLayout(Direction.VERTICAL))
.addComponent(new Label("- Pro pohyb mezi okny pouzij 'Tab' a 'Shift + Tab'."))
.addComponent(new Label("- Pokud je pritomen posuvnik, pouzij sipky pro posun."))
.addComponent(new Label("- Stiskni 'Space' pro specifikaci, napr skolniho roku, nebo pololeti."))
.addComponent(new Label("- Stiskni 'Escape' pro odhlaseni, smazani udaju a konec."))
.addComponent(new Label("- Nema smysl klikat mysi, vse je v terminalu."))
.addComponent(new EmptySpace())
.addComponent(new Label("Kdykoliv stiskni F1 pro zobrazeni tohoto okna."))
.addTo(mainPanel);
if(startupScreen)
{
mainPanel.addComponent(box);
}
new Panel()
.addComponent(
new Button(LocalizedString.OK.toString(), this::onOK)
.setLayoutData(GridLayout.createLayoutData(
GridLayout.Alignment.CENTER,
GridLayout.Alignment.CENTER,
true,
false)))
.addTo(mainPanel);
setHints(Arrays.asList(Window.Hint.CENTERED));
setComponent(mainPanel);
}
public void onOK()
{
close();
}
@Override
public Boolean showDialog(WindowBasedTextGUI textGUI) {
super.showDialog(textGUI);
return box.isChecked();
}
}

View File

@ -3,6 +3,7 @@ package xyz.thastertyn.UserInterface.Dialogs;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.googlecode.lanterna.gui2.Button;
@ -27,13 +28,21 @@ public class OptionsDialog extends DialogWindow {
public OptionsDialog(Options[] options)
{
super("Choose from below");
super("Vyber si");
boxs = new ArrayList<>();
for(Options o : options)
for(int i = 0; i < options.length; i++)
{
boxs.add(new ComboBox<Option>(o.getOptions()));
boxs.add(new ComboBox<Option>(options[i].getOptions()));
// Add the title of option to the dialogs title
if(options.length == 1 || i == 0)
{
setTitle(getTitle() + " \"" + options[i].getTitle() + "\"");
}else{
setTitle(getTitle() + " a \"" + options[i].getTitle() + "\"");
}
}
for(int i = 0; i < options.length; i++)
@ -47,12 +56,11 @@ public class OptionsDialog extends DialogWindow {
}
}
Panel mainPanel = new Panel()
.setLayoutManager(new GridLayout(1)
Panel mainPanel = new Panel(new GridLayout(1)
.setLeftMarginSize(1)
.setRightMarginSize(1));
Panel optionPanel = new Panel().setLayoutManager(new GridLayout(3))
Panel optionPanel = new Panel(new GridLayout(3))
.addTo(mainPanel);
@ -67,9 +75,7 @@ public class OptionsDialog extends DialogWindow {
}
}
new Panel()
.setLayoutManager(
new GridLayout(2).setHorizontalSpacing(1))
new Panel(new GridLayout(2).setHorizontalSpacing(1))
.addComponent(
new Button(LocalizedString.OK.toString(), this::onOK)
.setLayoutData(GridLayout.createLayoutData(
@ -98,17 +104,19 @@ public class OptionsDialog extends DialogWindow {
}
@Override
public Choice showDialog(WindowBasedTextGUI textGUI) {
public Optional<Choice> showDialog(WindowBasedTextGUI textGUI) {
super.showDialog(textGUI);
Choice c = new Choice(
boxs.stream()
.map(box -> box.getSelectedItem().getValue())
.collect(Collectors.toList()));
return (useData) ?
// User pressed Ok
new Choice(boxs.stream()
.map(box -> box.getSelectedItem().getValue())
.collect(Collectors.toList()))
// Why did .toList() stop working?
Optional.of(c)
:
// User pressed Cancel
null;
Optional.empty();
}
}

View File

@ -3,6 +3,8 @@ package xyz.thastertyn.UserInterface.Listeners;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.naming.ConfigurationException;
import com.googlecode.lanterna.TerminalPosition;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.gui2.Borders;
@ -18,12 +20,14 @@ import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton;
import com.googlecode.lanterna.input.KeyStroke;
import com.googlecode.lanterna.input.KeyType;
import xyz.thastertyn.UserInterface.Content.JecnaContent;
import xyz.thastertyn.Config.Config;
import xyz.thastertyn.UserInterface.Content.AbsenceList;
import xyz.thastertyn.UserInterface.Content.Timetable;
import xyz.thastertyn.UserInterface.Content.Reports;
import xyz.thastertyn.UserInterface.Content.JecnaContent;
import xyz.thastertyn.UserInterface.Content.Marks;
import xyz.thastertyn.UserInterface.Content.Reports;
import xyz.thastertyn.UserInterface.Content.Timetable;
import xyz.thastertyn.UserInterface.Dialogs.EscapeDialog;
import xyz.thastertyn.UserInterface.Dialogs.HelpDialog;
/**
* Holds all the content displayables and moves between them when needed on tab, or shift tab press
@ -41,8 +45,7 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
private Label[] tabs = new Label[contents.length];
private Panel tabsPanel = new Panel()
.setLayoutManager(new LinearLayout(Direction.HORIZONTAL));
private Panel tabsPanel = new Panel(new LinearLayout(Direction.HORIZONTAL));
private Panel holderPanel = new Panel();
int current = contents.length - 1;
@ -60,6 +63,43 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
}
next();
showStartHelp();
}
private void showStartHelp()
{
Config config = Config.getInstance();
boolean showDialog = true;
try {
String value = config.getConfig("dont_show_help_again");
if(value.equals("true"))
{
showDialog = false;
}
} catch (ConfigurationException e) {
e.printStackTrace();
}
if(showDialog)
{
HelpDialog dialog = new HelpDialog(true);
Boolean dontShowDialog = dialog.showDialog(textGUI);
if(dontShowDialog)
{
config.addConfig("dont_show_help_again", dontShowDialog.toString());
if(!config.writeConfig())
{
showException("Chyba ulozeni", "Nepodarilo se ulozit udaje");
}
}
}
}
private void next()
@ -104,7 +144,7 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
contents[current].downloadDefault();
}catch(IOException e)
{
MessageDialog.showMessageDialog(textGUI, "Something failed", e.getMessage(), MessageDialogButton.OK);
showException("Neco se nepovedlo", e.getLocalizedMessage());
}
}
}
@ -165,7 +205,7 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
contents[current].showOptions(textGUI);
}catch(IOException e)
{
MessageDialog.showMessageDialog(textGUI, "Something failed", "An error with connection has occured, either no connection at all, or connection timed out", MessageDialogButton.OK);
showException("Neco se nepovedlo", "Naskytl problem s pripojenim. Bud neni zadne, nebo prilis pomale.");
}
}
@ -182,7 +222,7 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
previous();
break;
case Character:
if(keyStroke.getCharacter() == ' ')
if(keyStroke.getCharacter() == ' ') // Space
{
showOptions();
}
@ -191,6 +231,10 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
EscapeDialog dialog = new EscapeDialog(textGUI, this);
dialog.showDialog(textGUI);
break;
case F1:
HelpDialog helpDialog = new HelpDialog(false);
helpDialog.showDialog(textGUI);
break;
default:
break;
}
@ -211,7 +255,9 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
window.invalidate();
}
/**
* Force a full redraw, usefull when a change via logout or {@link xyz.thastertyn.Types.Option} is used
*/
@Override
public void updatePanel() {
holderPanel.removeAllComponents();
@ -219,6 +265,11 @@ public class WindowSwitchListener implements WindowListener, UpdateListener, Con
holderPanel.addComponent(contents[current].getPanel());
}
private void showException(String title, String text)
{
MessageDialog.showMessageDialog(textGUI, title, text, MessageDialogButton.OK);
}
/**
* On user change should wipe everything
*/

View File

@ -55,8 +55,7 @@ public class MainWindow {
//#endregion
// Create panel to hold all components
final Panel mainPanel = new Panel();
mainPanel.setLayoutManager(new LinearLayout(Direction.VERTICAL));
final Panel mainPanel = new Panel(new LinearLayout(Direction.VERTICAL));
window.setComponent(mainPanel);

View File

@ -0,0 +1,82 @@
package xyz.thastertyn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import javax.naming.ConfigurationException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import xyz.thastertyn.Config.Config;
import xyz.thastertyn.Types.Credentials;
public class ConfigTest {
Config config = Config.getInstance();
File configFile;
File configFileBackup;
String path = config.getPath();
@Before
public void backupConfig() throws IOException
{
configFile = new File(path);
configFileBackup = new File(path + ".bak");
Files.copy(configFile.toPath(), configFileBackup.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
@After
public void moveConfigBack() throws IOException
{
Files.copy(configFileBackup.toPath(), configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
configFileBackup.delete();
}
@Test
public void checkIfExists()
{
config.addConfig("testKey", "testValue");
assertTrue(config.checkIfExists("testKey"));
config.removeConfig("testKey");
assertFalse(config.checkIfExists("testKey"));
}
@Test
public void checkCorrectKeyValue() throws ConfigurationException
{
config.addConfig("testKey2", "testValue2");
assertEquals("testValue2", config.getConfig("testKey2"));
}
@Test
public void checkIfCredentialsExist()
{
config.addConfig("user", "myusername");
config.addConfig("pass", "mypassword");
assertTrue(config.checkIfCredentialsExist());
config.removeCredentials();
assertFalse(config.checkIfCredentialsExist());
}
@Test
public void checkCorrectCredentials() throws ConfigurationException
{
config.addConfig("user", "MyUser123");
config.addConfig("pass", "VeryStrongP4SSWORD!#");
Credentials credentials = config.getCredentials();
assertNotNull(credentials);
assertEquals("MyUser123", credentials.getUsername());
assertEquals("VeryStrongP4SSWORD!#", credentials.getPassword());
}
}