Repository for the HealthTool which enables Apple users to analyse their health data from the Apple health app and prepares the data for contributing it for future studies on wearable data.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1736 lines
54 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package application;
  2. import java.io.BufferedInputStream;
  3. import java.io.BufferedOutputStream;
  4. import java.io.File;
  5. import java.io.FileOutputStream;
  6. import java.net.MalformedURLException;
  7. import java.net.URL;
  8. import java.nio.file.Files;
  9. import java.util.ArrayList;
  10. import java.util.HashMap;
  11. import java.util.List;
  12. import java.util.function.Predicate;
  13. import java.util.zip.ZipEntry;
  14. import java.util.zip.ZipFile;
  15. import application.customviews.EntryView;
  16. import application.customviews.FileView;
  17. import application.customviews.SubEntryView;
  18. import application.enums.EntryType;
  19. import application.enums.NavState;
  20. import application.helpers.ChangeViewCallback;
  21. import application.helpers.Utils;
  22. import application.helpers.WorkWhileProgressThread;
  23. import application.helpers.wrappers.Element;
  24. import application.helpers.wrappers.Occupation;
  25. import application.helpers.wrappers.WrappedException;
  26. import application.helpers.wrappers.WriteSelection;
  27. import application.parsing.ParsingWrapper;
  28. import application.parsing.ReadHandler;
  29. import application.res.Backgrounds;
  30. import application.res.Colors;
  31. import application.res.Occupations;
  32. import application.res.PrivacyStatementText;
  33. import application.res.Text;
  34. import javafx.application.HostServices;
  35. import javafx.beans.binding.Bindings;
  36. import javafx.beans.value.ChangeListener;
  37. import javafx.beans.value.ObservableValue;
  38. import javafx.collections.ObservableList;
  39. import javafx.event.Event;
  40. import javafx.event.EventHandler;
  41. import javafx.geometry.Insets;
  42. import javafx.geometry.Point2D;
  43. import javafx.scene.Cursor;
  44. import javafx.scene.Node;
  45. import javafx.scene.Scene;
  46. import javafx.scene.control.CheckBox;
  47. import javafx.scene.control.ComboBox;
  48. import javafx.scene.control.Label;
  49. import javafx.scene.control.ProgressIndicator;
  50. import javafx.scene.control.ScrollPane;
  51. import javafx.scene.control.TextArea;
  52. import javafx.scene.control.Tooltip;
  53. import javafx.scene.image.Image;
  54. import javafx.scene.image.ImageView;
  55. import javafx.scene.input.Clipboard;
  56. import javafx.scene.input.ClipboardContent;
  57. import javafx.scene.input.DataFormat;
  58. import javafx.scene.input.DragEvent;
  59. import javafx.scene.input.Dragboard;
  60. import javafx.scene.input.MouseEvent;
  61. import javafx.scene.input.TransferMode;
  62. import javafx.scene.layout.BorderPane;
  63. import javafx.scene.layout.GridPane;
  64. import javafx.scene.layout.HBox;
  65. import javafx.scene.layout.Pane;
  66. import javafx.scene.layout.Priority;
  67. import javafx.scene.layout.VBox;
  68. import javafx.scene.paint.Color;
  69. import javafx.scene.shape.Line;
  70. import javafx.stage.DirectoryChooser;
  71. import javafx.stage.Stage;
  72. import javafx.util.Duration;
  73. /**
  74. * Main class of the application. Changes the views, commands the work and
  75. * manages the navigation
  76. *
  77. * @author Bianca
  78. *
  79. */
  80. public class UICoordinator implements ChangeListener<Boolean>
  81. {
  82. /** Callback for interaction during the progressView**/
  83. private ChangeViewCallback cb;
  84. /** Scene of the application**/
  85. private Scene scene;
  86. /** The frame of the application**/
  87. private BorderPane frame;
  88. /** The name of the file containing the health data**/
  89. private String filename;
  90. /** The label for interaction with the drag and drop of the health data file **/
  91. private Label ddlabel;
  92. /** A label where text can be written (in the intro view) about the file not being correct **/
  93. private Label wrong_file;
  94. /** contains all data found in the health file to be displayed **/
  95. private VBox datalist;
  96. /** The stage of the application **/
  97. private Stage stage;
  98. /** Worker for all kinds of parsing**/
  99. private ParsingWrapper worker;
  100. /** Directory for saving the files which will be created**/
  101. private File dirForSaving;
  102. /** The filename of the file to be created **/
  103. private File fileForSaving;
  104. /** All files created in temp directory. Keys are the readable filenames**/
  105. private HashMap<String, File> filesInTempWithDesc;
  106. /** Checkbox for agreeing to the privacy policy (intro view)**/
  107. private CheckBox privAgree;
  108. /** count of elements selected in the overview**/
  109. private int selectCount = 0;
  110. /** The label for setting the filepath on where to save the created file **/
  111. private Label path;
  112. /** Handler which directs the user to the privacy view **/
  113. private EventHandler<MouseEvent> toPrivacyView;
  114. /** Host services of the system**/
  115. private HostServices hostServices;
  116. /** The job given by the user.**/
  117. private Occupation occupation;
  118. /** Text containing the information if files could not be written **/
  119. private String errorWritingFiles = "";
  120. // Navigation
  121. /** navigation element for the intro view**/
  122. private BorderPane start;
  123. /** navigation element for the overview view**/
  124. private BorderPane select;
  125. /** navigation element for the inspect view**/
  126. private BorderPane inspect;
  127. /** navigation element for the result view**/
  128. private BorderPane result;
  129. /** navigation element for the privacy view**/
  130. private BorderPane privacy;
  131. /**
  132. * Constructor which initializes the scene. It creates and sets the first view,
  133. * loads the stylesheet and creates the frame.
  134. */
  135. public UICoordinator()
  136. {
  137. cb = new ChangeViewCallback(this);
  138. datalist = new VBox();
  139. datalist.setId("datalist");
  140. datalist.setBorder(Utils.darkBlueBorder);
  141. ddlabel = new Label(Text.SELECT_FILE);
  142. wrong_file = new Label();
  143. wrong_file.setId("error");
  144. frame = setUpFrame();
  145. scene = new Scene(frame, 800, 650);
  146. frame.minHeightProperty().bind(Bindings.createDoubleBinding(() -> scene.getHeight(), scene.heightProperty()));
  147. frame.minWidthProperty().bind(Bindings.createDoubleBinding(() -> scene.getWidth(), scene.widthProperty()));
  148. changeView(getIntroView());
  149. String css = this.getClass().getResource("style.css").toExternalForm();
  150. scene.getStylesheets().add(css);
  151. }
  152. /**
  153. * Sets the attribute of the HostServices for this object
  154. * @param hs the hostServices of the system
  155. */
  156. public void setHostServices(HostServices hs)
  157. {
  158. this.hostServices = hs;
  159. }
  160. /**
  161. * Method initializes the frame: sets title, logo and the navigation bar
  162. *
  163. * @return initialized frame (left and top are frame)
  164. */
  165. private BorderPane setUpFrame()
  166. {
  167. BorderPane frame = new BorderPane();
  168. frame.setId("frame");
  169. //header
  170. BorderPane header = new BorderPane();
  171. Label title = new Label(Text.TITLE);
  172. header.setCenter(title);
  173. ImageView logo = new ImageView(new Image(this.getClass().getResourceAsStream("RI_Logo_weiss_en.png")));
  174. logo.setFitWidth(172.2 * 1.5); //these sizes are related to the image
  175. logo.setFitHeight(49.55 * 1.5);
  176. header.setLeft(logo);
  177. header.setId("frameheader");
  178. frame.setTop(header);
  179. //Navigation, labels on the right needed for tooltips
  180. VBox nav = new VBox();
  181. nav.setMinWidth(100);
  182. nav.setId("navigation");
  183. nav.setFillWidth(true);
  184. int buttonCount = 5;
  185. privacy = new BorderPane();
  186. privacy.minHeightProperty()
  187. .bind(Bindings.createDoubleBinding(() -> nav.getHeight() / buttonCount, nav.heightProperty()));
  188. ImageView dsgvoImage = new ImageView(new Image(this.getClass().getResourceAsStream("dsgvo.png")));
  189. privacy.setCenter(dsgvoImage);
  190. privacy.setRight(new Label());
  191. privacy.setUserData(Text.MENU_PRIVACY_POLICY);
  192. nav.getChildren().add(privacy);
  193. start = new BorderPane();
  194. ImageView startImage = new ImageView(new Image(this.getClass().getResourceAsStream("start2.png")));
  195. start.setCenter(startImage);
  196. start.minHeightProperty()
  197. .bind(Bindings.createDoubleBinding(() -> nav.getHeight() / buttonCount, nav.heightProperty()));
  198. start.setCenter(startImage);
  199. start.setRight(new Label());
  200. start.setUserData(Text.MENU_DATA_IMPORT);
  201. nav.getChildren().add(start);
  202. select = new BorderPane();
  203. select.minHeightProperty()
  204. .bind(Bindings.createDoubleBinding(() -> nav.getHeight() / buttonCount, nav.heightProperty()));
  205. ImageView selectImage = new ImageView(new Image(this.getClass().getResourceAsStream("select.png")));
  206. select.setCenter(selectImage);
  207. select.setRight(new Label());
  208. select.setUserData(Text.MENU_DATA_SELECTION);
  209. nav.getChildren().add(select);
  210. inspect = new BorderPane();
  211. inspect.minHeightProperty()
  212. .bind(Bindings.createDoubleBinding(() -> nav.getHeight() / buttonCount, nav.heightProperty()));
  213. ImageView inspectImage = new ImageView(new Image(this.getClass().getResourceAsStream("inspect.png")));
  214. inspect.setCenter(inspectImage);
  215. inspect.setRight(new Label());
  216. inspect.setUserData(Text.MENU_REVIEW);
  217. nav.getChildren().add(inspect);
  218. result = new BorderPane();
  219. result.minHeightProperty()
  220. .bind(Bindings.createDoubleBinding(() -> nav.getHeight() / buttonCount, nav.heightProperty()));
  221. ImageView resultImage = new ImageView(new Image(this.getClass().getResourceAsStream("result.png")));
  222. result.setCenter(resultImage);
  223. result.setRight(new Label());
  224. result.setUserData(Text.MENU_DATA_UPLOAD);
  225. nav.getChildren().add(result);
  226. frame.setLeft(nav);
  227. updateNav(NavState.INIT);
  228. //Footer
  229. BorderPane footer = new BorderPane();
  230. footer.setId("framefooter");
  231. footer.setCenter(new Label(Text.FOOTER_TEXT));
  232. frame.setBottom(footer);
  233. nav.minHeightProperty().bind(Bindings.createDoubleBinding(() -> frame.getHeight() - header.getHeight()
  234. - footer.getHeight() - Utils.darkBlueBorder.getInsets().getBottom() * 2, frame.heightProperty()));
  235. return frame;
  236. }
  237. /**
  238. * Adjusts the navigation to the state wanted
  239. * @param state new navigation state
  240. */
  241. public void updateNav(NavState state)
  242. {
  243. //handlers which will be used more than once
  244. //those are handlers for going to these views the *first* time with the current data
  245. EventHandler<MouseEvent> toOverview = new EventHandler<MouseEvent>()
  246. {
  247. @Override
  248. public void handle(MouseEvent arg0)
  249. {
  250. changeView(getProgressView(Text.WIP_READING_FILE));
  251. WorkWhileProgressThread t = new WorkWhileProgressThread(cb, true);
  252. t.start();
  253. }
  254. };
  255. EventHandler<MouseEvent> toIntroView = new EventHandler<MouseEvent>()
  256. {
  257. @Override
  258. public void handle(MouseEvent arg0)
  259. {
  260. changeView(getIntroView());
  261. updateNav(NavState.INIT);
  262. if (filename == null)
  263. {
  264. ddlabel.setText(Text.SELECT_FILE);
  265. }
  266. else
  267. {
  268. ddlabel.setText(String.format(Text.FOUND_FILE, filename));
  269. }
  270. }
  271. };
  272. EventHandler<MouseEvent> toInspectView = new EventHandler<MouseEvent>()
  273. {
  274. @Override
  275. public void handle(MouseEvent arg0)
  276. {
  277. changeView(getProgressView(Text.WIP_CREATE_FILE));
  278. WorkWhileProgressThread t = new WorkWhileProgressThread(cb, false);
  279. t.start();
  280. }
  281. };
  282. EventHandler<MouseEvent> toResultView = new EventHandler<MouseEvent>()
  283. {
  284. @Override
  285. public void handle(MouseEvent arg0)
  286. {
  287. fileForSaving = worker.writeToZipFile(filesInTempWithDesc, dirForSaving);
  288. changeView(getResultView());
  289. updateNav(NavState.RESULT);
  290. }
  291. };
  292. toPrivacyView = new EventHandler<MouseEvent>()
  293. {
  294. @Override
  295. public void handle(MouseEvent arg0)
  296. {
  297. changeView(getPrivacyView());
  298. updateNav(NavState.PRIVACY_STATEMENT);
  299. }
  300. };
  301. //these handlers are used for returning to a previous state
  302. EventHandler<MouseEvent> returnToIntroView = new EventHandler<MouseEvent>()
  303. {
  304. @Override
  305. public void handle(MouseEvent arg0)
  306. {
  307. changeView(getIntroView());
  308. updateNav(NavState.IMPORT_GIVEN);
  309. privAgree.setSelected(true);
  310. ddlabel.setText(String.format(Text.FOUND_FILE, filename));
  311. }
  312. };
  313. EventHandler<MouseEvent> returnToOverview = new EventHandler<MouseEvent>()
  314. {
  315. @Override
  316. public void handle(MouseEvent event)
  317. {
  318. changeView(getOverviewView());
  319. updateNav(NavState.DATA_AND_PATH_SELECTED);
  320. path.setText(String.format(Text.F_SAVE_AT,dirForSaving.getAbsolutePath()));
  321. }
  322. };
  323. //always active
  324. setNavElemActive(privacy, toPrivacyView);
  325. if (state == NavState.PRIVACY_STATEMENT)
  326. {
  327. setNavElemSelected(privacy, toPrivacyView);
  328. setNavElemDeselected(start);
  329. setNavElemDeselected(select);
  330. setNavElemDeselected(inspect);
  331. setNavElemDeselected(result);
  332. }
  333. if (state == NavState.INIT)
  334. {
  335. //first statment for tooltip
  336. setNavElemActive(start, returnToIntroView);
  337. setNavElemSelected(start, toIntroView);
  338. setNavElemInactive(select);
  339. setNavElemInactive(inspect);
  340. setNavElemInactive(result);
  341. }
  342. if (state == NavState.IMPORT_GIVEN)
  343. {
  344. setNavElemSelected(start, returnToIntroView);
  345. setNavElemActive(select, toOverview);
  346. setNavElemInactive(inspect);
  347. setNavElemInactive(result);
  348. }
  349. if (state == NavState.SELECT_DATA)
  350. {
  351. setNavElemActive(start, returnToIntroView);
  352. setNavElemSelected(select, toOverview);
  353. setNavElemInactive(inspect);
  354. setNavElemInactive(result);
  355. }
  356. if (state == NavState.DATA_AND_PATH_SELECTED)
  357. {
  358. setNavElemActive(start, returnToIntroView);
  359. setNavElemSelected(select, returnToOverview);
  360. setNavElemActive(inspect, toInspectView);
  361. setNavElemInactive(result);
  362. }
  363. if (state == NavState.START_AGAIN)
  364. {
  365. setNavElemSelected(start, returnToIntroView);
  366. setNavElemDeselected(select);
  367. setNavElemDeselected(inspect);
  368. setNavElemDeselected(result);
  369. }
  370. if (state == NavState.INSPECT)
  371. {
  372. setNavElemActive(start, returnToIntroView);
  373. setNavElemActive(select, returnToOverview);
  374. setNavElemSelected(inspect, toInspectView);
  375. setNavElemActive(result, toResultView);
  376. }
  377. if (state == NavState.INSPECT_WITH_ERROR)
  378. {
  379. setNavElemActive(start, returnToIntroView);
  380. setNavElemActive(select, returnToOverview);
  381. setNavElemSelected(inspect, toInspectView);
  382. setNavElemDeselected(result);
  383. }
  384. if (state == NavState.RESULT)
  385. {
  386. setNavElemActive(start, returnToIntroView);
  387. setNavElemActive(select, returnToOverview);
  388. setNavElemActive(inspect, toInspectView);
  389. setNavElemSelected(result, toResultView);
  390. }
  391. }
  392. /**
  393. * Sets a navigation element to be active. That makes it clickable.
  394. * @param element Element to be set active
  395. * @param onClick action to be done when it is clicked.
  396. */
  397. private void setNavElemActive(BorderPane element, EventHandler<MouseEvent> onClick)
  398. {
  399. element.setBackground(Backgrounds.darkBlueBackground);
  400. element.setBorder(null);
  401. Tooltip tooltip = new Tooltip(element.getUserData().toString());
  402. element.setOnMouseEntered(new EventHandler<MouseEvent>()
  403. {
  404. @Override
  405. public void handle(MouseEvent event)
  406. {
  407. Point2D p = element.localToScreen(element.getLayoutBounds().getMaxX(),
  408. element.getLayoutBounds().getMinY());
  409. ((Label)element.getRight()).setTooltip(tooltip);
  410. tooltip.show(((Label)element.getRight()), p.getX(), p.getY());
  411. tooltip.setAutoHide(true);
  412. }
  413. });
  414. element.setOnMouseExited(new EventHandler<MouseEvent>()
  415. {
  416. @Override
  417. public void handle(MouseEvent event)
  418. {
  419. tooltip.hide();
  420. }
  421. });
  422. element.setOnMouseClicked(onClick);
  423. }
  424. /**
  425. * Sets a navigation element as inactive
  426. * @param element the element which will be inactive
  427. */
  428. private void setNavElemInactive(BorderPane element)
  429. {
  430. element.setBackground(Backgrounds.greyBackground);
  431. element.setOnMouseEntered(null);
  432. element.setBorder(null);
  433. element.setOnMouseClicked(null);
  434. }
  435. /**
  436. * Selects a navigation element. It indicates the currently active view
  437. * @param element Navigation element to be selected
  438. * @param onClick Handler which should be executed in case it was clicked on
  439. */
  440. private void setNavElemSelected(BorderPane element, EventHandler<MouseEvent> onClick)
  441. {
  442. element.setBackground(Backgrounds.darkBlueBackground);
  443. element.setBorder(Utils.redBorder);
  444. element.setOnMouseClicked(onClick);
  445. }
  446. /**
  447. * Deselects a navigation element
  448. * @param element the element to be deselected
  449. */
  450. private void setNavElemDeselected(BorderPane element)
  451. {
  452. element.setBorder(null);
  453. }
  454. /**
  455. * Initialized the stage
  456. * @param stage
  457. */
  458. public void setStage(Stage stage)
  459. {
  460. this.stage = stage;
  461. stage.setScene(scene);
  462. stage.setTitle(Text.TITLE);
  463. stage.getIcons().add(new Image(this.getClass().getResourceAsStream("RI_Logo_blau_en_short.png")));
  464. stage.show();
  465. }
  466. /**
  467. * Switches the displayed view (always in a ScrollPane)
  468. * @param view The new view to be shown
  469. */
  470. public void changeView(Pane view)
  471. {
  472. ScrollPane scroll = new ScrollPane();
  473. scroll.setContent(view);
  474. scroll.setId("scroll");
  475. frame.setCenter(scroll);
  476. scroll.getStyleClass().add("edge-to-edge");
  477. view.minWidthProperty()
  478. .bind(Bindings.createDoubleBinding(() -> frame.getCenter().minWidth(0), frame.widthProperty()));
  479. scroll.setFitToWidth(true);
  480. }
  481. /**
  482. * Returns the scene
  483. * @return the described value
  484. */
  485. public Scene getScene()
  486. {
  487. return scene;
  488. }
  489. /**
  490. * Creates the first view which is shown after starting the application. It
  491. * allows the user to insert a health file and check the privacy policy
  492. *
  493. * @return the created view
  494. */
  495. private VBox getIntroView()
  496. {
  497. VBox container = new VBox();
  498. container.setId("container");
  499. container.getChildren().add(new Label(Text.MAIN_INFO));
  500. Label appleLink = new Label(Text.APPLE_LINK_EXPORT);
  501. container.getChildren().add(appleLink);
  502. appleLink.setTextFill(Colors.grey);
  503. appleLink.setOnMouseEntered(new EventHandler<Event>()
  504. {
  505. @Override
  506. public void handle(Event event)
  507. {
  508. appleLink.setTextFill(Colors.blue);
  509. }
  510. });
  511. appleLink.setOnMouseExited(new EventHandler<Event>()
  512. {
  513. @Override
  514. public void handle(Event event)
  515. {
  516. appleLink.setTextFill(Colors.grey);
  517. }
  518. });
  519. appleLink.setOnMouseClicked(new EventHandler<Event>()
  520. {
  521. @Override
  522. public void handle(Event event)
  523. {
  524. try
  525. {
  526. hostServices.showDocument(new URL(Text.APPLE_LINK_EXPORT).toExternalForm());
  527. }
  528. catch (MalformedURLException e)
  529. {
  530. //do nth
  531. }
  532. }
  533. });
  534. //Allow drag and drop for the health file
  535. HBox ddTarget = new HBox();
  536. ddTarget.setId("ddTarget");
  537. ddlabel.setId("ddLabel");
  538. ddTarget.minWidthProperty()
  539. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  540. ddlabel.minWidthProperty()
  541. .bind(Bindings.createDoubleBinding(() -> ddTarget.getWidth(), ddTarget.widthProperty()));
  542. ddTarget.getChildren().add(ddlabel);
  543. ddTarget.setOnDragOver(new EventHandler<DragEvent>()
  544. {
  545. @Override
  546. public void handle(DragEvent event)
  547. {
  548. if (event.getGestureSource() != ddTarget && event.getDragboard().hasFiles())
  549. {
  550. event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
  551. }
  552. event.consume();
  553. }
  554. });
  555. ddTarget.setOnDragDropped(new EventHandler<DragEvent>()
  556. {
  557. @Override
  558. public void handle(DragEvent event)
  559. {
  560. Dragboard db = event.getDragboard();
  561. boolean success = false;
  562. if (db.hasFiles())
  563. {
  564. filename = db.getFiles().toString().replace("[", "").replace("]", "");
  565. ddlabel.setText(String.format(Text.FOUND_FILE, filename));
  566. checkFile(filename);
  567. success = true;
  568. }
  569. event.setDropCompleted(success);
  570. event.consume();
  571. }
  572. });
  573. container.getChildren().add(ddTarget);
  574. container.getChildren().add(wrong_file);
  575. //check privacy policy
  576. privAgree = new CheckBox();
  577. privAgree.setText(Text.PRIVACY_STATEMENT_ACCEPTED);
  578. privAgree.setWrapText(true);
  579. privAgree.setStyle("-fx-font-family: \"Arial\"");
  580. privAgree.selectedProperty().addListener(new ChangeListener<Boolean>()
  581. {
  582. @Override
  583. public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
  584. {
  585. checkFile(filename);
  586. }
  587. });
  588. privAgree.minWidthProperty()
  589. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  590. container.getChildren().add(privAgree);
  591. //Button to be directed to the privacy view
  592. HBox gotToPrivacyView = new HBox();
  593. gotToPrivacyView.setPrefHeight(50);
  594. gotToPrivacyView.setId("goToPriv");
  595. BorderPane button = new BorderPane();
  596. button.setCenter(new Label(Text.GO_TO_PRIVACY_STATEMENT));
  597. button.setOnMouseClicked(toPrivacyView);
  598. button.setId("goToPrivButton");
  599. gotToPrivacyView.getChildren().addAll(button);
  600. HBox.setHgrow(button, Priority.ALWAYS);
  601. container.getChildren().add(gotToPrivacyView);
  602. return container;
  603. }
  604. /**
  605. * checks on a superficial level whether the input health file is correct
  606. * (fileType) and adjusts the navigation
  607. *
  608. * @param filename Filename to check
  609. */
  610. private void checkFile(String filename)
  611. {
  612. if (filename != null && (filename.endsWith(".xml") || filename.endsWith(".zip")))
  613. {
  614. if (privAgree.isSelected())
  615. {
  616. updateNav(NavState.IMPORT_GIVEN);
  617. }
  618. else
  619. {
  620. updateNav(NavState.INIT);
  621. }
  622. wrong_file.setText("");
  623. }
  624. else if (filename != null)
  625. {
  626. updateNav(NavState.INIT);
  627. wrong_file.setText(Text.NO_VALID_FILE);
  628. }
  629. }
  630. /**
  631. * Creates a progress view to be shown when the application needs some time
  632. * working
  633. *
  634. * @param text Text to be displayed in the view
  635. * @return the created view
  636. */
  637. public VBox getProgressView(String text)
  638. {
  639. VBox container = new VBox();
  640. container.setId("container");
  641. Label l = new Label(text);
  642. container.getChildren().add(l);
  643. ProgressIndicator PI = new ProgressIndicator();
  644. container.getChildren().add(PI);
  645. return container;
  646. }
  647. /**
  648. * This creates the third view which shows the created files in temp dir and
  649. * allows the user to inspect them. Don't change any widths and heights here!!!
  650. *
  651. * @return the created view
  652. */
  653. public VBox getInspectView()
  654. {
  655. VBox container = new VBox();
  656. container.setId("container");
  657. container.setPadding(new Insets(0, 0, 5, 0));
  658. Label l = new Label(Text.PLEASE_INSPECT);
  659. container.getChildren().add(l);
  660. GridPane files = new GridPane();
  661. files.setBorder(Utils.darkBlueBorder);
  662. int oneFileViewWidth = FileView.WIDTH;
  663. int filesBorderWidth = (int) (files.getBorder().getInsets().getLeft()
  664. + files.getBorder().getInsets().getRight());
  665. container.getChildren().add(files);
  666. files.maxWidthProperty().bind(Bindings.createDoubleBinding(() ->
  667. // windowWidth - (border*2) = width for fileViews
  668. // floor(width for fileViews/Fileview.width) = number of possible views
  669. // number of possible views*FileView.width = needed width for fileViews
  670. // needed width for fileViews+ borders = complete width
  671. (Math.floor((container.getWidth() - filesBorderWidth) / oneFileViewWidth) * oneFileViewWidth)
  672. // borders of the FlowPane
  673. + filesBorderWidth, container.widthProperty()));
  674. // if the container width changes, the number of views should also change
  675. container.widthProperty().addListener(new ChangeListener<Number>()
  676. {
  677. @Override
  678. public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
  679. {
  680. int oneFileViewWidth = FileView.WIDTH;
  681. int filesBorderWidth = (int) (files.getBorder().getInsets().getLeft()
  682. + files.getBorder().getInsets().getRight());
  683. int col = 0;
  684. int row = 0;
  685. int colElems = (int) ((container.getWidth() - filesBorderWidth) / oneFileViewWidth);
  686. if (filesInTempWithDesc != null)
  687. {
  688. files.getChildren().clear();
  689. for (String desc : filesInTempWithDesc.keySet())
  690. {
  691. files.add(new FileView(filesInTempWithDesc.get(desc), desc, hostServices), col, row);
  692. col++;
  693. if (col == colElems)
  694. {
  695. row++;
  696. col = 0;
  697. }
  698. }
  699. }
  700. else
  701. {
  702. files.getChildren().add(new Label(errorWritingFiles));
  703. }
  704. }
  705. });
  706. return container;
  707. }
  708. /**
  709. * Creates the second view which contains all the information of the health data
  710. *
  711. * @return created view
  712. */
  713. public VBox getOverviewView()
  714. {
  715. VBox container = new VBox();
  716. container.setId("container");
  717. // Label selectText = new Label(Text.SELECT_CATEGORIES);
  718. // container.getChildren().add(selectText);
  719. //
  720. container.getChildren().add(new Label(Text.SELECT_INTRO));
  721. // input the job
  722. container.getChildren().add(getJobSelection(container));
  723. // View for clicking and setting the path for saving the resulting data
  724. BorderPane selectSavePath = new BorderPane();
  725. String currentUsersHomeDir = System.getProperty("user.home");
  726. if(dirForSaving==null)
  727. {
  728. dirForSaving= new File(currentUsersHomeDir);
  729. }
  730. path = new Label(String.format(Text.F_LABEL_SAVE_PATH, currentUsersHomeDir));
  731. selectSavePath.setCenter(path);
  732. path.minWidthProperty()
  733. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  734. path.setOnMouseClicked(new EventHandler<MouseEvent>()
  735. {
  736. @Override
  737. public void handle(MouseEvent event)
  738. {
  739. DirectoryChooser dirChooser = new DirectoryChooser();
  740. dirChooser.setTitle("Save Data");
  741. File var = dirChooser.showDialog(stage);
  742. if (var != null)
  743. {
  744. dirForSaving = var;
  745. path.setText(String.format(Text.F_SAVE_AT, dirForSaving.getPath()));
  746. if (selectCount > 0)
  747. {
  748. updateNav(NavState.DATA_AND_PATH_SELECTED);
  749. }
  750. }
  751. }
  752. });
  753. path.setOnMouseEntered(new EventHandler<Event>()
  754. {
  755. @Override
  756. public void handle(Event event)
  757. {
  758. path.setTextFill(Colors.blue);
  759. }
  760. });
  761. path.setOnMouseExited(new EventHandler<Event>()
  762. {
  763. @Override
  764. public void handle(Event event)
  765. {
  766. path.setTextFill(Color.BLACK);
  767. }
  768. });
  769. container.getChildren().add(selectSavePath);
  770. container.getChildren().add(new Label(Text.SELECT_HOW_TO));
  771. // These are the buttons to help selection
  772. HBox selectButtons = new HBox();
  773. VBox.setVgrow(selectButtons, Priority.ALWAYS);
  774. selectButtons.setId("selectButtons");
  775. BorderPane selectAll = new BorderPane();
  776. selectAll.setCenter(new Label(Text.SELECT_ALL));
  777. selectAll.setOnMouseClicked(new EventHandler<MouseEvent>()
  778. {
  779. @Override
  780. public void handle(MouseEvent arg0)
  781. {
  782. for (Node n : datalist.getChildren())
  783. {
  784. if (n instanceof EntryView)
  785. {
  786. EntryView i = (EntryView) n;
  787. i.setSelected(true, true);
  788. }
  789. }
  790. }
  791. });
  792. BorderPane deselectAll = new BorderPane();
  793. deselectAll.setCenter(new Label(Text.DESELECT_ALL));
  794. deselectAll.setOnMouseClicked(new EventHandler<MouseEvent>()
  795. {
  796. @Override
  797. public void handle(MouseEvent arg0)
  798. {
  799. updateNav(NavState.SELECT_DATA);
  800. for (Node n : datalist.getChildren())
  801. {
  802. if (n instanceof EntryView)
  803. {
  804. EntryView i = (EntryView) n;
  805. i.setSelected(false, true);
  806. }
  807. }
  808. }
  809. });
  810. BorderPane selectCurrentResearchMinimum = new BorderPane();
  811. Label l = new Label(Text.SELECT_BASIC_FIELD_OF_INTEREST);
  812. l.setWrapText(true);
  813. selectCurrentResearchMinimum.minHeightProperty()
  814. .bind(Bindings.createDoubleBinding(() -> l.getMaxHeight(), l.maxHeightProperty()));
  815. selectCurrentResearchMinimum.maxHeightProperty()
  816. .bind(Bindings.createDoubleBinding(() -> l.getMaxHeight(), l.maxHeightProperty()));
  817. selectCurrentResearchMinimum.minWidthProperty()
  818. .bind(Bindings.createDoubleBinding(() -> l.getWidth(), l.widthProperty()));
  819. selectCurrentResearchMinimum.setCenter(l);
  820. selectCurrentResearchMinimum.setOnMouseClicked(new EventHandler<MouseEvent>()
  821. {
  822. @Override
  823. public void handle(MouseEvent arg0)
  824. {
  825. for (Node n : datalist.getChildren())
  826. {
  827. if (n instanceof EntryView)
  828. {
  829. EntryView i = (EntryView) n;
  830. i.setSelected(false, true);
  831. }
  832. }
  833. for (Node n : datalist.getChildren())
  834. {
  835. if (n instanceof EntryView)
  836. {
  837. EntryView i = (EntryView) n;
  838. // Top level
  839. // biologicalSex, regionCode
  840. if (i.getEntryType().equals(EntryType.BIOLOGICAL_SEX)
  841. || i.getEntryType().equals(EntryType.REGION_CODE))
  842. {
  843. i.setSelected(true, true);
  844. }
  845. // sub elements
  846. // year of birth, stepCount,
  847. if (i.getEntryType().equals(EntryType.DATE_OF_BIRTH))
  848. {
  849. i.selectSubelement(Text.YEAR);
  850. }
  851. if (i.getEntryType().equals(EntryType.RECORD))
  852. {
  853. i.selectSubelement("StepCount");
  854. }
  855. }
  856. }
  857. }
  858. });
  859. BorderPane selectCurrentResearchAdvanced = new BorderPane();
  860. Label lAdvanced = new Label(Text.SELECT_ADVANCED_FIELD_OF_INTEREST);
  861. lAdvanced.setWrapText(true);
  862. selectCurrentResearchAdvanced.minWidthProperty()
  863. .bind(Bindings.createDoubleBinding(() -> lAdvanced.getWidth(), lAdvanced.widthProperty()));
  864. selectCurrentResearchAdvanced.setCenter(lAdvanced);
  865. selectCurrentResearchAdvanced.setOnMouseClicked(new EventHandler<MouseEvent>()
  866. {
  867. @Override
  868. public void handle(MouseEvent arg0)
  869. {
  870. for (Node n : datalist.getChildren())
  871. {
  872. if (n instanceof EntryView)
  873. {
  874. EntryView i = (EntryView) n;
  875. i.setSelected(false, true);
  876. }
  877. }
  878. for (Node n : datalist.getChildren())
  879. {
  880. if (n instanceof EntryView)
  881. {
  882. EntryView i = (EntryView) n;
  883. // Top level
  884. if (i.getEntryType().equals(EntryType.BIOLOGICAL_SEX)
  885. || i.getEntryType().equals(EntryType.REGION_CODE))
  886. {
  887. i.setSelected(true, true);
  888. }
  889. // sub elements
  890. if (i.getEntryType().equals(EntryType.DATE_OF_BIRTH))
  891. {
  892. i.selectSubelement(Text.YEAR);
  893. }
  894. if (i.getEntryType().equals(EntryType.RECORD))
  895. {
  896. i.selectSubelement("StepCount");
  897. i.selectSubelement("RestingHeartRate");
  898. i.selectSubelement("BodyMass");
  899. i.selectSubelement("Height");
  900. i.selectSubelement("RespiratoryRate");
  901. i.selectSubelement("SwimmingStrokeCount");
  902. i.selectSubelement("DistanceSwimming");
  903. i.selectSubelement("SleepAnalysis");
  904. i.selectSubelement("DistanceWalkingRunning");
  905. i.selectSubelement("HeartRate");
  906. i.selectSubelement("DistanceCycling");
  907. i.selectSubelement("HeartRateVariabilitySDNN");
  908. }
  909. if (i.getEntryType().equals(EntryType.WORKOUT))
  910. {
  911. i.selectSubelement("Cycling");
  912. i.selectSubelement("Walking");
  913. i.selectSubelement("Running");
  914. i.selectSubelement("Swimming");
  915. }
  916. }
  917. }
  918. }
  919. });
  920. selectButtons.getChildren().addAll(selectAll, selectCurrentResearchAdvanced, selectCurrentResearchMinimum,deselectAll);
  921. //-30 here needed because of padding and spacing etc.
  922. selectAll.minWidthProperty()
  923. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  924. deselectAll.minWidthProperty()
  925. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  926. selectCurrentResearchAdvanced.minWidthProperty()
  927. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  928. selectCurrentResearchMinimum.minWidthProperty()
  929. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  930. //Fix for apple: boxes are too big on init without this
  931. selectAll.maxHeightProperty()
  932. .bind(Bindings.createDoubleBinding(() ->80.0, selectAll.heightProperty()));
  933. selectCurrentResearchMinimum.maxHeightProperty()
  934. .bind(Bindings.createDoubleBinding(() ->80.0, selectCurrentResearchMinimum.heightProperty()));
  935. selectCurrentResearchAdvanced.maxHeightProperty()
  936. .bind(Bindings.createDoubleBinding(() ->80.0, selectCurrentResearchAdvanced.heightProperty()));
  937. deselectAll.maxHeightProperty()
  938. .bind(Bindings.createDoubleBinding(() ->80.0, deselectAll.heightProperty()));
  939. container.getChildren().add(selectButtons);
  940. deselectAll.requestFocus();
  941. // Here is the data, but its only drawn here. Loading of the data is done
  942. // somewhere else
  943. BorderPane datacontainer = new BorderPane();
  944. datacontainer.setPadding(new Insets(5));
  945. datacontainer.setId("datacontainer");
  946. datacontainer.setCenter(datalist);
  947. container.getChildren().add(datacontainer);
  948. datacontainer.minWidthProperty()
  949. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  950. datacontainer.maxWidthProperty()
  951. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  952. datalist.minWidthProperty()
  953. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  954. datalist.maxWidthProperty()
  955. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  956. return container;
  957. }
  958. /**
  959. * Returns the matching occupation list from {@link Occupations}, inserts
  960. * {@code Text.NOT_SET} and selects a subset from the prefix (if level >2)
  961. * @param prefix The prefix of the string (number combination)
  962. * to define the subset. The code of the super group.
  963. * @return The described value, {@code null} if value < 0 or > 4
  964. */
  965. private ObservableList<String> getOccupationList( String prefix)
  966. {
  967. Predicate<String> prefixPred = new Predicate<String>()
  968. {
  969. @Override
  970. public boolean test(String t)
  971. {
  972. return t.startsWith(prefix)||t.equals(Text.NOT_SET);
  973. }
  974. };
  975. if(prefix.length()==0)
  976. {
  977. return Occupations.JOBS_LEVEL_1;
  978. }
  979. else if(prefix.length()==1)
  980. {
  981. return Occupations.JOBS_LEVEL_2.filtered(prefixPred);
  982. }
  983. else if(prefix.length()==2)
  984. {
  985. return Occupations.JOBS_LEVEL_3.filtered(prefixPred);
  986. }
  987. else if(prefix.length()==3)
  988. {
  989. return Occupations.JOBS_LEVEL_4.filtered(prefixPred);
  990. }
  991. return null;
  992. }
  993. /**
  994. * This method creates a styled comboBox for the occupations
  995. * @param container For the width binding
  996. * @return the created comboBox
  997. */
  998. private ComboBox<String> getComboBox(Pane container)
  999. {
  1000. ComboBox<String> box= new ComboBox<>();
  1001. box.setDisable(true);
  1002. box.setBackground(Backgrounds.lightGreyBackground);
  1003. box.minWidthProperty()
  1004. .bind(Bindings.createDoubleBinding(() -> container.getWidth()-(2*container.getBorder().getInsets().getRight()), container.widthProperty()));
  1005. box.maxWidthProperty()
  1006. .bind(Bindings.createDoubleBinding(() -> container.getWidth()-(2*container.getBorder().getInsets().getRight()), container.widthProperty()));
  1007. return box;
  1008. }
  1009. /**
  1010. * Method creates and prepares the dropdown selections for the jobs.
  1011. * @param container Container for binding the width to
  1012. * @return A pane containing the dropdowns and info texts
  1013. */
  1014. private Pane getJobSelection(Pane container)
  1015. {
  1016. Label addJob = new Label(Text.ADD_JOB);
  1017. VBox comboContainer = new VBox();
  1018. comboContainer.setBorder(Utils.darkBlueBorder);
  1019. ComboBox<String> levelOne = getComboBox(comboContainer);
  1020. ComboBox<String> levelTwo = getComboBox(comboContainer);
  1021. ComboBox<String> levelThree = getComboBox(comboContainer);
  1022. ComboBox<String> levelFour = getComboBox(comboContainer);
  1023. levelOne.setStyle("-fx-font-family :\"Arial\"");
  1024. levelTwo.setStyle("-fx-font-family :\"Arial\"");
  1025. levelThree.setStyle("-fx-font-family :\"Arial\"");
  1026. levelFour.setStyle("-fx-font-family :\"Arial\"");
  1027. levelOne.setItems(getOccupationList(""));
  1028. levelOne.setDisable(false);
  1029. levelOne.setValue(Text.NOT_SET);
  1030. occupation= new Occupation(Text.NOT_SET);
  1031. levelOne.valueProperty().addListener(new ChangeListener<String>()
  1032. {
  1033. @Override
  1034. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1035. {
  1036. occupation.setLevelOne(newValue);
  1037. if(newValue.equals(Text.NOT_SET))
  1038. {
  1039. levelTwo.setDisable(true);
  1040. occupation.setLevelTwo(null);
  1041. levelTwo.setItems(null);
  1042. }
  1043. else
  1044. {
  1045. String prefix = ""+newValue.charAt(0);
  1046. levelTwo.setItems(getOccupationList(prefix));
  1047. levelTwo.setDisable(false);
  1048. levelTwo.setValue(Text.NOT_SET);
  1049. }
  1050. }
  1051. });
  1052. levelTwo.valueProperty().addListener(new ChangeListener<String>()
  1053. {
  1054. @Override
  1055. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1056. {
  1057. occupation.setLevelTwo(newValue);
  1058. if(newValue==null ||newValue.equals(Text.NOT_SET))
  1059. {
  1060. levelThree.setItems(null);
  1061. occupation.setLevelThree(null);
  1062. levelThree.setDisable(true);
  1063. }
  1064. else
  1065. {
  1066. String prefix = ""+newValue.substring(0,2);
  1067. levelThree.setItems(getOccupationList( prefix));
  1068. levelThree.setDisable(false);
  1069. levelThree.setValue(Text.NOT_SET);
  1070. }
  1071. }
  1072. });
  1073. levelThree.valueProperty().addListener(new ChangeListener<String>()
  1074. {
  1075. @Override
  1076. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1077. {
  1078. occupation.setLevelThree(newValue);
  1079. if(newValue==null ||newValue.equals(Text.NOT_SET))
  1080. {
  1081. occupation.setLevelFour(null);
  1082. levelFour.setItems(null);
  1083. levelFour.setDisable(true);
  1084. }
  1085. else
  1086. {
  1087. String prefix = newValue.substring(0,3);
  1088. levelFour.setItems(getOccupationList( prefix));
  1089. levelFour.setDisable(false);
  1090. levelFour.setValue(Text.NOT_SET);
  1091. }
  1092. }
  1093. });
  1094. levelFour.valueProperty().addListener(new ChangeListener<String>(){
  1095. @Override
  1096. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1097. {
  1098. occupation.setLevelFour(newValue);
  1099. }});
  1100. comboContainer.getChildren().add(levelOne);
  1101. comboContainer.getChildren().add(levelTwo);
  1102. comboContainer.getChildren().add(levelThree);
  1103. comboContainer.getChildren().add(levelFour);
  1104. HBox jobInfo = new HBox();
  1105. jobInfo.setId("jobInfo");
  1106. jobInfo.minWidthProperty()
  1107. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1108. jobInfo.maxWidthProperty()
  1109. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1110. jobInfo.maxHeightProperty()
  1111. .bind(Bindings.createDoubleBinding(() -> jobInfo.getPrefHeight(), jobInfo.prefHeightProperty()));
  1112. comboContainer.minHeightProperty()
  1113. .bind(Bindings.createDoubleBinding(() -> addJob.getHeight(), addJob.heightProperty()));
  1114. addJob.maxWidthProperty()
  1115. .bind(Bindings.createDoubleBinding(() -> container.getWidth()/2, container.widthProperty()));
  1116. HBox.setHgrow(comboContainer,Priority.ALWAYS);
  1117. jobInfo.getChildren().add(comboContainer);
  1118. jobInfo.getChildren().add(addJob);
  1119. // VBox occupationSelection = new VBox();
  1120. // occupationSelection.setBorder(Utils.darkBlueBorder);
  1121. // occupationSelection.getChildren().add(jobInfo);
  1122. // occupationSelection.getChildren().add(new OccupationSelectionView(1, getOccupationList("")));
  1123. return jobInfo;
  1124. }
  1125. /**
  1126. * The health data file is read and the overview of all found data is included
  1127. * in the datalist
  1128. *
  1129. * @throws WrappedException if the file coulnd't be read
  1130. */
  1131. public void fillDataList() throws WrappedException
  1132. {
  1133. datalist.getChildren().clear();
  1134. File file;
  1135. if (filename.contains(".zip"))
  1136. { // a zip file can't be read just like this and so the containing file needs to
  1137. // be copied into temp
  1138. try
  1139. {
  1140. ZipFile f = new ZipFile(filename);
  1141. String xmlFileName = "apple_health_export/Export.xml";
  1142. file = Files.createTempFile("health_data", "__Export.xml").toFile();
  1143. file.deleteOnExit();
  1144. ZipEntry xml = f.getEntry(xmlFileName);
  1145. if (xml == null)
  1146. {
  1147. xml = f.getEntry(xmlFileName.toLowerCase());
  1148. }
  1149. try (BufferedInputStream is = new BufferedInputStream(f.getInputStream(xml));
  1150. BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)))
  1151. {
  1152. byte[] b = new byte[1024];
  1153. int res = is.read(b);
  1154. while (res > -1)
  1155. {
  1156. out.write(b, 0, res);
  1157. res = is.read(b);
  1158. }
  1159. }
  1160. f.close();
  1161. }
  1162. catch (Exception e)
  1163. {
  1164. // The zip-File couldn't be read, maybe it was the wrong file or error
  1165. throw new WrappedException(e, Text.E_READ_ZIP_FILE);
  1166. }
  1167. }
  1168. else
  1169. {
  1170. file = new File(filename);
  1171. }
  1172. try
  1173. {
  1174. worker = new ParsingWrapper(file);
  1175. }
  1176. catch (Exception e)
  1177. {
  1178. throw new WrappedException(e, Text.E_READ_XML_FILE);
  1179. }
  1180. ReadHandler read = worker.getRead();
  1181. // an EntryView is created per EntryType (ME)
  1182. for (EntryType type : EntryType.ALL_ME_ONES)
  1183. {
  1184. EntryView ev;
  1185. if (type.equals(EntryType.EXPORT_DATE))
  1186. {
  1187. ev = new EntryView(new Element(type, read.getExportDate()));
  1188. }
  1189. else if (type.equals(EntryType.REGION_CODE))
  1190. {
  1191. ev = new EntryView(new Element(type, read.getRegionCode()));
  1192. }
  1193. else if (type == EntryType.DATE_OF_BIRTH)
  1194. {
  1195. String value = read.getDateOfBirth();
  1196. if (value == null || value.equals("") || value.equals(Text.NOT_SET))
  1197. { // Don't add an element when there is no data given
  1198. continue;
  1199. }
  1200. ev = new EntryView(new Element(type, read.getDateOfBirth()), read.getSplitDateOfBirth());
  1201. }
  1202. else
  1203. {
  1204. String value = read.getMeInfo(type);
  1205. if (value == null || value.equals("") || value.equals(Text.NOT_SET))
  1206. { // Don't add an element when there is no data given
  1207. continue;
  1208. }
  1209. ev = new EntryView(new Element(type, value));
  1210. }
  1211. ev.maxWidthProperty()
  1212. .bind(Bindings.createDoubleBinding(
  1213. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1214. datalist.widthProperty()));
  1215. ev.minWidthProperty()
  1216. .bind(Bindings.createDoubleBinding(
  1217. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1218. datalist.widthProperty()));
  1219. datalist.getChildren().add(ev);
  1220. datalist.getChildren().add(getSeparator(datalist));
  1221. ev.addCheckListener(this);
  1222. }
  1223. // an EntryView is created per EntryType (not ME)
  1224. for (EntryType type : EntryType.ALL_SEPARATE_ONES)
  1225. {
  1226. int n = read.getTypeNumber(type);
  1227. if (n > 0)
  1228. {
  1229. EntryView ev;
  1230. if (type == EntryType.WORKOUT)
  1231. {
  1232. ev = new EntryView(new Element(type, n + ""), read.getSubWorkouts());
  1233. }
  1234. else if (type == EntryType.RECORD)
  1235. {
  1236. ev = new EntryView(new Element(type, n + ""), read.getSubRecords());
  1237. }
  1238. else
  1239. {
  1240. ev = new EntryView(new Element(type, n + ""));
  1241. }
  1242. ev.addCheckListener(this);
  1243. ev.maxWidthProperty()
  1244. .bind(Bindings.createDoubleBinding(
  1245. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1246. datalist.widthProperty()));
  1247. ev.minWidthProperty()
  1248. .bind(Bindings.createDoubleBinding(
  1249. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1250. datalist.widthProperty()));
  1251. datalist.getChildren().add(ev);
  1252. datalist.getChildren().add(getSeparator(datalist));
  1253. }
  1254. }
  1255. if (datalist.getChildren().size() > 0) // something inside, so there is a separator too much
  1256. {
  1257. datalist.getChildren().remove(datalist.getChildren().size() - 1);
  1258. }
  1259. }
  1260. /**
  1261. * Used to display in the overview when the initial health file couldn't be
  1262. * read. Consists of a BorderPane with a text in its center
  1263. *
  1264. * @param text Text to be displayed
  1265. * @return the created BorderPane
  1266. */
  1267. private BorderPane getEmptyFileView(String text)
  1268. {
  1269. BorderPane p = new BorderPane();
  1270. p.setCenter(new Label(text));
  1271. return p;
  1272. }
  1273. /**
  1274. * In case there was an error reading the inital health file, the overview will
  1275. * only show an information text regarding the error
  1276. *
  1277. * @param text Text to be displayed
  1278. */
  1279. public void setErrorReadingFile(String text)
  1280. {
  1281. datalist.getChildren().add(getEmptyFileView(text));
  1282. }
  1283. /**
  1284. * Saves the error which occurred when the temp files are created
  1285. *
  1286. * @param text Details on why it didn't work
  1287. */
  1288. public void setErrorWritingTempFiles(String text)
  1289. {
  1290. filesInTempWithDesc = null;
  1291. errorWritingFiles = text;
  1292. }
  1293. /**
  1294. * Creates a horizontal separator line to be used in lists
  1295. *
  1296. * @param toBind The view it should be bound to (length wise)
  1297. * @return The created separator
  1298. */
  1299. private BorderPane getSeparator(Pane toBind)
  1300. {
  1301. BorderPane p = new BorderPane();
  1302. Line line = new Line();
  1303. line.setStartX(0);
  1304. line.setStartY(0);
  1305. line.setEndY(0);
  1306. line.endXProperty().bind(Bindings.createDoubleBinding(
  1307. () -> toBind.getWidth() - toBind.getBorder().getInsets().getRight() * 2, toBind.widthProperty()));
  1308. line.setStroke(Colors.darkGrey);
  1309. p.setCenter(line);
  1310. return p;
  1311. }
  1312. /**
  1313. * Creates the last view showing the overview of all saved data and the help to
  1314. * upload the created zip file
  1315. **/
  1316. public VBox getResultView()
  1317. {
  1318. VBox container = new VBox();
  1319. container.setId("container");
  1320. Label done = new Label(Text.SUCCESS);
  1321. container.getChildren().add(done);
  1322. // buttons for easier access to nextcloud
  1323. HBox nextcloudHelp = new HBox();
  1324. nextcloudHelp.setId("nextcloudHelp");
  1325. nextcloudHelp.minWidthProperty()
  1326. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1327. // open in browser
  1328. BorderPane goToNextcloud = new BorderPane();
  1329. goToNextcloud.setId("goToNextcloud");
  1330. Label nextcloudText = new Label(Text.NEXTCLOUD);
  1331. goToNextcloud.setCenter(nextcloudText);
  1332. goToNextcloud.setLeft(new ImageView(
  1333. new Image(this.getClass().
  1334. getResourceAsStream("internet.png"),64.0,64.0, true, true)));
  1335. goToNextcloud.setOnMouseClicked(new EventHandler<MouseEvent>()
  1336. { // open in browser if possible
  1337. @Override
  1338. public void handle(MouseEvent arg0)
  1339. {
  1340. try
  1341. {
  1342. hostServices.showDocument(new URL(Text.NEXTCLOUD_ADDRESS).toExternalForm());
  1343. }
  1344. catch (MalformedURLException e)
  1345. { // otherwise do nothing
  1346. }
  1347. }
  1348. });
  1349. nextcloudHelp.getChildren().add(goToNextcloud);
  1350. List<File> clipboardFiles = new ArrayList<>();
  1351. clipboardFiles.add(fileForSaving);
  1352. // copy filepath to clipboard
  1353. BorderPane copyToClipboard = new BorderPane();
  1354. copyToClipboard.setId("copyToClipboard");
  1355. Label clipBoardText = new Label(Text.COPY_FILE_PATH);
  1356. copyToClipboard.setCenter(clipBoardText);
  1357. copyToClipboard.setLeft(new ImageView(
  1358. new Image(this.getClass().
  1359. getResourceAsStream("filepath.png"),64.0,64.0, true, true)));
  1360. copyToClipboard.setOnMouseClicked(new EventHandler<MouseEvent>()
  1361. {
  1362. @Override
  1363. public void handle(MouseEvent arg0)
  1364. {
  1365. final Clipboard clipboard = Clipboard.getSystemClipboard();
  1366. final ClipboardContent content = new ClipboardContent();
  1367. content.putString(fileForSaving.getAbsolutePath());
  1368. clipboard.setContent(content);
  1369. Point2D p = clipBoardText.localToScreen(clipBoardText.getLayoutBounds().getCenterX(),
  1370. clipBoardText.getLayoutBounds().getCenterY());
  1371. Tooltip copied = new Tooltip(Text.FILE_PATH_IN_CLIPBOARD);
  1372. clipBoardText.setTooltip(copied);
  1373. copied.show(clipBoardText, p.getX(), p.getY());
  1374. copied.setHideDelay(Duration.seconds(5));
  1375. copied.autoHideProperty().set(true);
  1376. }
  1377. });
  1378. // drag file to browser
  1379. BorderPane dragToNextcloud = new BorderPane();
  1380. dragToNextcloud.setId("dragToServer");
  1381. dragToNextcloud.setCenter(new Label(Text.DRAG_FILE));
  1382. dragToNextcloud.setLeft(new ImageView(
  1383. new Image(this.getClass().
  1384. getResourceAsStream("archive.png"),64.0,64.0, true, true)));
  1385. dragToNextcloud.setOnMouseEntered(new EventHandler<MouseEvent>()
  1386. {
  1387. @Override
  1388. public void handle(MouseEvent event)
  1389. {
  1390. scene.setCursor(Cursor.OPEN_HAND);
  1391. }
  1392. });
  1393. dragToNextcloud.setOnMouseExited(new EventHandler<MouseEvent>()
  1394. {
  1395. @Override
  1396. public void handle(MouseEvent event)
  1397. {
  1398. scene.setCursor(Cursor.DEFAULT);
  1399. }
  1400. });
  1401. dragToNextcloud.setOnDragDetected(new EventHandler<MouseEvent>()
  1402. {
  1403. @Override
  1404. public void handle(MouseEvent event)
  1405. {
  1406. /* drag was detected, start a drag-and-drop gesture */
  1407. /* allow any transfer mode */
  1408. Dragboard db = dragToNextcloud.startDragAndDrop(TransferMode.ANY);
  1409. /* Put a string on a dragboard */
  1410. ClipboardContent content = new ClipboardContent();
  1411. content.put(DataFormat.FILES, clipboardFiles);
  1412. // content.putString(source.getText());
  1413. db.setContent(content);
  1414. event.consume();
  1415. }
  1416. });
  1417. HBox.setHgrow(copyToClipboard, Priority.ALWAYS);
  1418. HBox.setHgrow(dragToNextcloud, Priority.ALWAYS);
  1419. HBox.setHgrow(goToNextcloud, Priority.ALWAYS);
  1420. nextcloudHelp.getChildren().add(dragToNextcloud);
  1421. nextcloudHelp.getChildren().add(copyToClipboard);
  1422. container.getChildren().add(nextcloudHelp);
  1423. TextArea copyable = new TextArea(String.format(Text.F_RAW_UPLOAD_DATA, fileForSaving.getAbsolutePath(),Text.NEXTCLOUD_ADDRESS));
  1424. copyable.setEditable(false);
  1425. copyable.setWrapText(true);
  1426. copyable.setStyle("-fx-font-family :\"Arial\"");
  1427. copyable.setId("buttonProblems");
  1428. copyable.setFocusTraversable(false);
  1429. copyable.setPrefHeight(70);
  1430. container.getChildren().add(copyable);
  1431. // table to show all saved data
  1432. VBox overview = new VBox();
  1433. overview.minWidthProperty()
  1434. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1435. container.getChildren().add(overview);
  1436. overview.setId("savedOverview");
  1437. overview.setBorder(Utils.darkBlueBorder);
  1438. // header
  1439. Label savedOverview = new Label(Text.DATA_OVERVIEW);
  1440. savedOverview.minWidthProperty()
  1441. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1442. overview.getChildren().add(savedOverview);
  1443. savedOverview.setStyle("-fx-font-weight: bold");
  1444. // add job
  1445. if (!occupation.getLevelOne().equals(Text.NOT_SET))
  1446. {
  1447. Label x = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1448. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_ONE, occupation.getLevelOne()));
  1449. x.minWidthProperty()
  1450. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1451. overview.getChildren().add(x);
  1452. overview.getChildren().add(getSeparator(overview));
  1453. if (!occupation.getLevelTwo().equals(Text.NOT_SET))
  1454. {
  1455. Label x2 = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1456. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_TWO, occupation.getLevelTwo()));
  1457. x2.minWidthProperty()
  1458. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1459. overview.getChildren().add(x2);
  1460. overview.getChildren().add(getSeparator(overview));
  1461. if (!occupation.getLevelThree().equals(Text.NOT_SET))
  1462. {
  1463. Label x3 = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1464. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_THREE, occupation.getLevelThree()));
  1465. x3.minWidthProperty()
  1466. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1467. overview.getChildren().add(x3);
  1468. overview.getChildren().add(getSeparator(overview));
  1469. if (!occupation.getLevelFour().equals(Text.NOT_SET))
  1470. {
  1471. Label x4 = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1472. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_FOUR, occupation.getLevelFour()));
  1473. x4.minWidthProperty()
  1474. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1475. overview.getChildren().add(x4);
  1476. overview.getChildren().add(getSeparator(overview));
  1477. }
  1478. }
  1479. }
  1480. }
  1481. // add all selected Data
  1482. for (Node n : datalist.getChildren())
  1483. {
  1484. if (n instanceof EntryView) // this should always be true
  1485. {
  1486. EntryView i = (EntryView) n;
  1487. EntryType type = i.getEntryType();
  1488. if (i.getSelected())
  1489. {
  1490. if (type.equals(EntryType.WORKOUT) || type.equals(EntryType.RECORD)
  1491. || type.equals(EntryType.DATE_OF_BIRTH))
  1492. { // selected ones with subelements
  1493. for (SubEntryView s : i.getSelectedSubElements())
  1494. {
  1495. Label x = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY_ENTRIES,
  1496. type.getValue(), Utils.shortenHKStrings(s.getName()), s.getValue()));
  1497. if (type.equals(EntryType.DATE_OF_BIRTH))
  1498. {
  1499. x = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY, type.getValue(),
  1500. s.getName(), s.getValue()));
  1501. }
  1502. x.minWidthProperty().bind(
  1503. Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1504. overview.getChildren().add(x);
  1505. overview.getChildren().add(getSeparator(overview));
  1506. }
  1507. }
  1508. else
  1509. { // selected ones without subelements
  1510. String value = i.getValue();
  1511. Label x = new Label(String.format(Text.F_SAVED_DATA_VALUE, type.getValue(), value));
  1512. if (type.equals(EntryType.ACTIVITY_SUMMARY))
  1513. {
  1514. x = new Label(String.format(Text.F_SAVED_DATA_ENTRIES, type.getValue(), value));
  1515. }
  1516. x.minWidthProperty().bind(
  1517. Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1518. overview.getChildren().add(x);
  1519. overview.getChildren().add(getSeparator(overview));
  1520. }
  1521. }
  1522. }
  1523. }
  1524. return container;
  1525. }
  1526. /**
  1527. * creates the view to show the privacy policy
  1528. *
  1529. * @return
  1530. */
  1531. public VBox getPrivacyView()
  1532. {
  1533. VBox container = new VBox();
  1534. container.setId("container");
  1535. container.getChildren().add(getHeaderLabel(PrivacyStatementText.heading,22));
  1536. container.getChildren().add(getContentLabel(PrivacyStatementText.responsibleParty, container));
  1537. container.getChildren().add(getContentLabel(PrivacyStatementText.introText, container));
  1538. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec1, 16));
  1539. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec1, container));
  1540. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec2, 16));
  1541. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec2, container));
  1542. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec3, 16));
  1543. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec3, container));
  1544. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec4, 16));
  1545. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec4, container));
  1546. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec5, 16));
  1547. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec5, container));
  1548. return container;
  1549. }
  1550. private Label getHeaderLabel(String text, int size)
  1551. {
  1552. Label res = new Label(text);
  1553. res.setStyle("-fx-font-weight: bold; -fx-font-size: "+size+"px;");
  1554. return res;
  1555. }
  1556. /**
  1557. * Creates a label for the Privacy view which is used for normal text content. Fills the whole width of its parent
  1558. * @param text Text to be displayed
  1559. * @param container Parent to set the width
  1560. * @return created label
  1561. */
  1562. private Label getContentLabel(String text, Pane container)
  1563. {
  1564. Label res = new Label(text);
  1565. res.minWidthProperty()
  1566. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1567. return res;
  1568. }
  1569. /**
  1570. * Creates the files in the temp directory
  1571. *
  1572. * @throws WrappedException
  1573. */
  1574. public void createTempFiles() throws WrappedException
  1575. {
  1576. filesInTempWithDesc = worker.writeSelectedDataToFiles(new WriteSelection(datalist), occupation);
  1577. }
  1578. @Override
  1579. public void changed(ObservableValue<? extends Boolean> checkbox, Boolean oldValue, Boolean newValue)
  1580. {
  1581. if (newValue)
  1582. {
  1583. selectCount++;
  1584. }
  1585. else
  1586. {
  1587. selectCount--;
  1588. }
  1589. if (selectCount > 0 && dirForSaving != null)
  1590. {
  1591. updateNav(NavState.DATA_AND_PATH_SELECTED);
  1592. }
  1593. else
  1594. {
  1595. updateNav(NavState.SELECT_DATA);
  1596. }
  1597. }
  1598. }