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.

1716 lines
53 KiB

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