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.

1717 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. e.printStackTrace();
  530. }
  531. }
  532. });
  533. //Allow drag and drop for the health file
  534. HBox ddTarget = new HBox();
  535. ddTarget.setId("ddTarget");
  536. ddlabel.setId("ddLabel");
  537. ddTarget.minWidthProperty()
  538. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  539. ddlabel.minWidthProperty()
  540. .bind(Bindings.createDoubleBinding(() -> ddTarget.getWidth(), ddTarget.widthProperty()));
  541. ddTarget.getChildren().add(ddlabel);
  542. ddTarget.setOnDragOver(new EventHandler<DragEvent>()
  543. {
  544. @Override
  545. public void handle(DragEvent event)
  546. {
  547. if (event.getGestureSource() != ddTarget && event.getDragboard().hasFiles())
  548. {
  549. event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
  550. }
  551. event.consume();
  552. }
  553. });
  554. ddTarget.setOnDragDropped(new EventHandler<DragEvent>()
  555. {
  556. @Override
  557. public void handle(DragEvent event)
  558. {
  559. Dragboard db = event.getDragboard();
  560. boolean success = false;
  561. if (db.hasFiles())
  562. {
  563. filename = db.getFiles().toString().replace("[", "").replace("]", "");
  564. ddlabel.setText(String.format(Text.FOUND_FILE, filename));
  565. checkFile(filename);
  566. success = true;
  567. }
  568. event.setDropCompleted(success);
  569. event.consume();
  570. }
  571. });
  572. container.getChildren().add(ddTarget);
  573. container.getChildren().add(wrong_file);
  574. //check privacy policy
  575. privAgree = new CheckBox();
  576. privAgree.setText(Text.PRIVACY_STATEMENT_ACCEPTED);
  577. privAgree.setWrapText(true);
  578. privAgree.selectedProperty().addListener(new ChangeListener<Boolean>()
  579. {
  580. @Override
  581. public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
  582. {
  583. checkFile(filename);
  584. }
  585. });
  586. privAgree.minWidthProperty()
  587. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  588. container.getChildren().add(privAgree);
  589. //Button to be directed to the privacy view
  590. HBox gotToPrivacyView = new HBox();
  591. gotToPrivacyView.setPrefHeight(50);
  592. gotToPrivacyView.setId("goToPriv");
  593. BorderPane button = new BorderPane();
  594. button.setCenter(new Label(Text.GO_TO_PRIVACY_STATEMENT));
  595. button.setOnMouseClicked(toPrivacyView);
  596. button.setId("goToPrivButton");
  597. gotToPrivacyView.getChildren().addAll(button);
  598. HBox.setHgrow(button, Priority.ALWAYS);
  599. container.getChildren().add(gotToPrivacyView);
  600. return container;
  601. }
  602. /**
  603. * checks on a superficial level whether the input health file is correct
  604. * (fileType) and adjusts the navigation
  605. *
  606. * @param filename Filename to check
  607. */
  608. private void checkFile(String filename)
  609. {
  610. if (filename != null && (filename.endsWith(".xml") || filename.endsWith(".zip")))
  611. {
  612. if (privAgree.isSelected())
  613. {
  614. updateNav(NavState.IMPORT_GIVEN);
  615. }
  616. else
  617. {
  618. updateNav(NavState.INIT);
  619. }
  620. wrong_file.setText("");
  621. }
  622. else if (filename != null)
  623. {
  624. updateNav(NavState.INIT);
  625. wrong_file.setText(Text.NO_VALID_FILE);
  626. }
  627. }
  628. /**
  629. * Creates a progress view to be shown when the application needs some time
  630. * working
  631. *
  632. * @param text Text to be displayed in the view
  633. * @return the created view
  634. */
  635. public VBox getProgressView(String text)
  636. {
  637. VBox container = new VBox();
  638. container.setId("container");
  639. Label l = new Label(text);
  640. container.getChildren().add(l);
  641. ProgressIndicator PI = new ProgressIndicator();
  642. container.getChildren().add(PI);
  643. return container;
  644. }
  645. /**
  646. * This creates the third view which shows the created files in temp dir and
  647. * allows the user to inspect them. Don't change any widths and heights here!!!
  648. *
  649. * @return the created view
  650. */
  651. public VBox getInspectView()
  652. {
  653. VBox container = new VBox();
  654. container.setId("container");
  655. container.setPadding(new Insets(0, 0, 5, 0));
  656. Label l = new Label(Text.PLEASE_INSPECT);
  657. container.getChildren().add(l);
  658. GridPane files = new GridPane();
  659. files.setBorder(Utils.darkBlueBorder);
  660. int oneFileViewWidth = FileView.WIDTH;
  661. int filesBorderWidth = (int) (files.getBorder().getInsets().getLeft()
  662. + files.getBorder().getInsets().getRight());
  663. container.getChildren().add(files);
  664. files.maxWidthProperty().bind(Bindings.createDoubleBinding(() ->
  665. // windowWidth - (border*2) = width for fileViews
  666. // floor(width for fileViews/Fileview.width) = number of possible views
  667. // number of possible views*FileView.width = needed width for fileViews
  668. // needed width for fileViews+ borders = complete width
  669. (Math.floor((container.getWidth() - filesBorderWidth) / oneFileViewWidth) * oneFileViewWidth)
  670. // borders of the FlowPane
  671. + filesBorderWidth, container.widthProperty()));
  672. // if the container width changes, the number of views should also change
  673. container.widthProperty().addListener(new ChangeListener<Number>()
  674. {
  675. @Override
  676. public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
  677. {
  678. int oneFileViewWidth = FileView.WIDTH;
  679. int filesBorderWidth = (int) (files.getBorder().getInsets().getLeft()
  680. + files.getBorder().getInsets().getRight());
  681. int col = 0;
  682. int row = 0;
  683. int colElems = (int) ((container.getWidth() - filesBorderWidth) / oneFileViewWidth);
  684. if (filesInTempWithDesc != null)
  685. {
  686. files.getChildren().clear();
  687. for (String desc : filesInTempWithDesc.keySet())
  688. {
  689. files.add(new FileView(filesInTempWithDesc.get(desc), desc, hostServices), col, row);
  690. col++;
  691. if (col == colElems)
  692. {
  693. row++;
  694. col = 0;
  695. }
  696. }
  697. }
  698. else
  699. {
  700. files.getChildren().add(new Label(errorWritingFiles));
  701. }
  702. }
  703. });
  704. return container;
  705. }
  706. /**
  707. * Creates the second view which contains all the information of the health data
  708. *
  709. * @return created view
  710. */
  711. public VBox getOverviewView()
  712. {
  713. VBox container = new VBox();
  714. container.setId("container");
  715. // Label selectText = new Label(Text.SELECT_CATEGORIES);
  716. // container.getChildren().add(selectText);
  717. //
  718. container.getChildren().add(new Label(Text.SELECT_INTRO));
  719. // input the job
  720. container.getChildren().add(getJobSelection(container));
  721. // View for clicking and setting the path for saving the resulting data
  722. BorderPane selectSavePath = new BorderPane();
  723. String currentUsersHomeDir = System.getProperty("user.home");
  724. dirForSaving= new File(currentUsersHomeDir);
  725. path = new Label(String.format(Text.F_LABEL_SAVE_PATH, currentUsersHomeDir));
  726. selectSavePath.setCenter(path);
  727. path.minWidthProperty()
  728. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  729. path.setOnMouseClicked(new EventHandler<MouseEvent>()
  730. {
  731. @Override
  732. public void handle(MouseEvent event)
  733. {
  734. DirectoryChooser dirChooser = new DirectoryChooser();
  735. dirChooser.setTitle("Save Data");
  736. File var = dirChooser.showDialog(stage);
  737. if (var != null)
  738. {
  739. dirForSaving = var;
  740. path.setText(String.format(Text.F_SAVE_AT, dirForSaving.getPath()));
  741. if (selectCount > 0)
  742. {
  743. updateNav(NavState.DATA_AND_PATH_SELECTED);
  744. }
  745. }
  746. }
  747. });
  748. path.setOnMouseEntered(new EventHandler<Event>()
  749. {
  750. @Override
  751. public void handle(Event event)
  752. {
  753. path.setTextFill(Colors.blue);
  754. }
  755. });
  756. path.setOnMouseExited(new EventHandler<Event>()
  757. {
  758. @Override
  759. public void handle(Event event)
  760. {
  761. path.setTextFill(Color.BLACK);
  762. }
  763. });
  764. container.getChildren().add(selectSavePath);
  765. container.getChildren().add(new Label(Text.SELECT_HOW_TO));
  766. // These are the buttons to help selection
  767. HBox selectButtons = new HBox();
  768. VBox.setVgrow(selectButtons, Priority.ALWAYS);
  769. selectButtons.setId("selectButtons");
  770. BorderPane selectAll = new BorderPane();
  771. selectAll.setCenter(new Label(Text.SELECT_ALL));
  772. selectAll.setOnMouseClicked(new EventHandler<MouseEvent>()
  773. {
  774. @Override
  775. public void handle(MouseEvent arg0)
  776. {
  777. for (Node n : datalist.getChildren())
  778. {
  779. if (n instanceof EntryView)
  780. {
  781. EntryView i = (EntryView) n;
  782. i.setSelected(true, true);
  783. }
  784. }
  785. }
  786. });
  787. BorderPane deselectAll = new BorderPane();
  788. deselectAll.setCenter(new Label(Text.DESELECT_ALL));
  789. deselectAll.setOnMouseClicked(new EventHandler<MouseEvent>()
  790. {
  791. @Override
  792. public void handle(MouseEvent arg0)
  793. {
  794. updateNav(NavState.SELECT_DATA);
  795. for (Node n : datalist.getChildren())
  796. {
  797. if (n instanceof EntryView)
  798. {
  799. EntryView i = (EntryView) n;
  800. i.setSelected(false, true);
  801. }
  802. }
  803. }
  804. });
  805. BorderPane selectCurrentResearchMinimum = new BorderPane();
  806. Label l = new Label(Text.SELECT_BASIC_FIELD_OF_INTEREST);
  807. l.setWrapText(true);
  808. selectCurrentResearchMinimum.minHeightProperty()
  809. .bind(Bindings.createDoubleBinding(() -> l.getMaxHeight(), l.maxHeightProperty()));
  810. selectCurrentResearchMinimum.maxHeightProperty()
  811. .bind(Bindings.createDoubleBinding(() -> l.getMaxHeight(), l.maxHeightProperty()));
  812. selectCurrentResearchMinimum.minWidthProperty()
  813. .bind(Bindings.createDoubleBinding(() -> l.getWidth(), l.widthProperty()));
  814. selectCurrentResearchMinimum.setCenter(l);
  815. selectCurrentResearchMinimum.setOnMouseClicked(new EventHandler<MouseEvent>()
  816. {
  817. @Override
  818. public void handle(MouseEvent arg0)
  819. {
  820. for (Node n : datalist.getChildren())
  821. {
  822. if (n instanceof EntryView)
  823. {
  824. EntryView i = (EntryView) n;
  825. i.setSelected(false, true);
  826. }
  827. }
  828. for (Node n : datalist.getChildren())
  829. {
  830. if (n instanceof EntryView)
  831. {
  832. EntryView i = (EntryView) n;
  833. // Top level
  834. // biologicalSex, regionCode
  835. if (i.getEntryType().equals(EntryType.BIOLOGICAL_SEX)
  836. || i.getEntryType().equals(EntryType.REGION_CODE))
  837. {
  838. i.setSelected(true, true);
  839. }
  840. // sub elements
  841. // year of birth, stepCount,
  842. if (i.getEntryType().equals(EntryType.DATE_OF_BIRTH))
  843. {
  844. i.selectSubelement(Text.YEAR);
  845. }
  846. if (i.getEntryType().equals(EntryType.RECORD))
  847. {
  848. i.selectSubelement("StepCount");
  849. }
  850. }
  851. }
  852. }
  853. });
  854. BorderPane selectCurrentResearchAdvanced = new BorderPane();
  855. Label lAdvanced = new Label(Text.SELECT_ADVANCED_FIELD_OF_INTEREST);
  856. lAdvanced.setWrapText(true);
  857. selectCurrentResearchAdvanced.minWidthProperty()
  858. .bind(Bindings.createDoubleBinding(() -> lAdvanced.getWidth(), lAdvanced.widthProperty()));
  859. selectCurrentResearchAdvanced.setCenter(lAdvanced);
  860. selectCurrentResearchAdvanced.setOnMouseClicked(new EventHandler<MouseEvent>()
  861. {
  862. @Override
  863. public void handle(MouseEvent arg0)
  864. {
  865. for (Node n : datalist.getChildren())
  866. {
  867. if (n instanceof EntryView)
  868. {
  869. EntryView i = (EntryView) n;
  870. i.setSelected(false, true);
  871. }
  872. }
  873. for (Node n : datalist.getChildren())
  874. {
  875. if (n instanceof EntryView)
  876. {
  877. EntryView i = (EntryView) n;
  878. // Top level
  879. if (i.getEntryType().equals(EntryType.BIOLOGICAL_SEX)
  880. || i.getEntryType().equals(EntryType.REGION_CODE))
  881. {
  882. i.setSelected(true, true);
  883. }
  884. // sub elements
  885. if (i.getEntryType().equals(EntryType.DATE_OF_BIRTH))
  886. {
  887. i.selectSubelement(Text.YEAR);
  888. }
  889. if (i.getEntryType().equals(EntryType.RECORD))
  890. {
  891. i.selectSubelement("StepCount");
  892. i.selectSubelement("RestingHeartRate");
  893. i.selectSubelement("BodyMass");
  894. i.selectSubelement("Height");
  895. i.selectSubelement("RespiratoryRate");
  896. i.selectSubelement("SwimmingStrokeCount");
  897. i.selectSubelement("DistanceSwimming");
  898. i.selectSubelement("SleepAnalysis");
  899. i.selectSubelement("DistanceWalkingRunning");
  900. i.selectSubelement("HeartRate");
  901. i.selectSubelement("DistanceCycling");
  902. i.selectSubelement("HeartRateVariabilitySDNN");
  903. }
  904. if (i.getEntryType().equals(EntryType.WORKOUT))
  905. {
  906. i.selectSubelement("Cycling");
  907. i.selectSubelement("Walking");
  908. i.selectSubelement("Running");
  909. i.selectSubelement("Swimming");
  910. }
  911. }
  912. }
  913. }
  914. });
  915. selectButtons.getChildren().addAll(selectAll, selectCurrentResearchAdvanced, selectCurrentResearchMinimum,deselectAll);
  916. //-30 here needed because of padding and spacing etc.
  917. selectAll.minWidthProperty()
  918. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  919. deselectAll.minWidthProperty()
  920. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  921. selectCurrentResearchAdvanced.minWidthProperty()
  922. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  923. selectCurrentResearchMinimum.minWidthProperty()
  924. .bind(Bindings.createDoubleBinding(() ->(selectButtons.getWidth()-30)/4, selectButtons.widthProperty()));
  925. //Fix for apple: boxes are too big on init without this
  926. selectAll.maxHeightProperty()
  927. .bind(Bindings.createDoubleBinding(() ->80.0, selectAll.heightProperty()));
  928. selectCurrentResearchMinimum.maxHeightProperty()
  929. .bind(Bindings.createDoubleBinding(() ->80.0, selectCurrentResearchMinimum.heightProperty()));
  930. selectCurrentResearchAdvanced.maxHeightProperty()
  931. .bind(Bindings.createDoubleBinding(() ->80.0, selectCurrentResearchAdvanced.heightProperty()));
  932. deselectAll.maxHeightProperty()
  933. .bind(Bindings.createDoubleBinding(() ->80.0, deselectAll.heightProperty()));
  934. container.getChildren().add(selectButtons);
  935. deselectAll.requestFocus();
  936. // Here is the data, but its only drawn here. Loading of the data is done
  937. // somewhere else
  938. BorderPane datacontainer = new BorderPane();
  939. datacontainer.setPadding(new Insets(5));
  940. datacontainer.setId("datacontainer");
  941. datacontainer.setCenter(datalist);
  942. container.getChildren().add(datacontainer);
  943. datacontainer.minWidthProperty()
  944. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  945. datacontainer.maxWidthProperty()
  946. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  947. datalist.minWidthProperty()
  948. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  949. datalist.maxWidthProperty()
  950. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  951. return container;
  952. }
  953. /**
  954. * Returns the matching occupation list from {@link Occupations}, inserts
  955. * {@code Text.NOT_SET} and selects a subset from the prefix (if level >2)
  956. * @param prefix The prefix of the string (number combination)
  957. * to define the subset. The code of the super group.
  958. * @return The described value, {@code null} if value < 0 or > 4
  959. */
  960. private ObservableList<String> getOccupationList( String prefix)
  961. {
  962. Predicate<String> prefixPred = new Predicate<String>()
  963. {
  964. @Override
  965. public boolean test(String t)
  966. {
  967. return t.startsWith(prefix)||t.equals(Text.NOT_SET);
  968. }
  969. };
  970. if(prefix.length()==0)
  971. {
  972. return Occupations.JOBS_LEVEL_1;
  973. }
  974. else if(prefix.length()==1)
  975. {
  976. return Occupations.JOBS_LEVEL_2.filtered(prefixPred);
  977. }
  978. else if(prefix.length()==2)
  979. {
  980. return Occupations.JOBS_LEVEL_3.filtered(prefixPred);
  981. }
  982. else if(prefix.length()==3)
  983. {
  984. return Occupations.JOBS_LEVEL_4.filtered(prefixPred);
  985. }
  986. return null;
  987. }
  988. /**
  989. * This method creates a styled comboBox for the occupations
  990. * @param container For the width binding
  991. * @return the created comboBox
  992. */
  993. private ComboBox<String> getComboBox(Pane container)
  994. {
  995. ComboBox<String> box= new ComboBox<>();
  996. box.setDisable(true);
  997. box.setBackground(Backgrounds.lightGreyBackground);
  998. box.minWidthProperty()
  999. .bind(Bindings.createDoubleBinding(() -> container.getWidth()-(2*container.getBorder().getInsets().getRight()), container.widthProperty()));
  1000. box.maxWidthProperty()
  1001. .bind(Bindings.createDoubleBinding(() -> container.getWidth()-(2*container.getBorder().getInsets().getRight()), container.widthProperty()));
  1002. return box;
  1003. }
  1004. /**
  1005. * Method creates and prepares the dropdown selections for the jobs.
  1006. * @param container Container for binding the width to
  1007. * @return A pane containing the dropdowns and info texts
  1008. */
  1009. private Pane getJobSelection(Pane container)
  1010. {
  1011. Label addJob = new Label(Text.ADD_JOB);
  1012. VBox comboContainer = new VBox();
  1013. comboContainer.setBorder(Utils.darkBlueBorder);
  1014. ComboBox<String> levelOne = getComboBox(comboContainer);
  1015. ComboBox<String> levelTwo = getComboBox(comboContainer);
  1016. ComboBox<String> levelThree = getComboBox(comboContainer);
  1017. ComboBox<String> levelFour = getComboBox(comboContainer);
  1018. levelOne.setItems(getOccupationList(""));
  1019. levelOne.setDisable(false);
  1020. levelOne.setValue(Text.NOT_SET);
  1021. occupation= new Occupation(Text.NOT_SET);
  1022. levelOne.valueProperty().addListener(new ChangeListener<String>()
  1023. {
  1024. @Override
  1025. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1026. {
  1027. occupation.setLevelOne(newValue);
  1028. if(newValue.equals(Text.NOT_SET))
  1029. {
  1030. levelTwo.setDisable(true);
  1031. occupation.setLevelTwo(null);
  1032. levelTwo.setItems(null);
  1033. }
  1034. else
  1035. {
  1036. String prefix = ""+newValue.charAt(0);
  1037. levelTwo.setItems(getOccupationList(prefix));
  1038. levelTwo.setDisable(false);
  1039. levelTwo.setValue(Text.NOT_SET);
  1040. }
  1041. }
  1042. });
  1043. levelTwo.valueProperty().addListener(new ChangeListener<String>()
  1044. {
  1045. @Override
  1046. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1047. {
  1048. occupation.setLevelTwo(newValue);
  1049. if(newValue==null ||newValue.equals(Text.NOT_SET))
  1050. {
  1051. levelThree.setItems(null);
  1052. occupation.setLevelThree(null);
  1053. levelThree.setDisable(true);
  1054. }
  1055. else
  1056. {
  1057. String prefix = ""+newValue.substring(0,2);
  1058. levelThree.setItems(getOccupationList( prefix));
  1059. levelThree.setDisable(false);
  1060. levelThree.setValue(Text.NOT_SET);
  1061. }
  1062. }
  1063. });
  1064. levelThree.valueProperty().addListener(new ChangeListener<String>()
  1065. {
  1066. @Override
  1067. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1068. {
  1069. occupation.setLevelThree(newValue);
  1070. if(newValue==null ||newValue.equals(Text.NOT_SET))
  1071. {
  1072. occupation.setLevelFour(null);
  1073. levelFour.setItems(null);
  1074. levelFour.setDisable(true);
  1075. }
  1076. else
  1077. {
  1078. String prefix = newValue.substring(0,3);
  1079. levelFour.setItems(getOccupationList( prefix));
  1080. levelFour.setDisable(false);
  1081. levelFour.setValue(Text.NOT_SET);
  1082. }
  1083. }
  1084. });
  1085. levelFour.valueProperty().addListener(new ChangeListener<String>(){
  1086. @Override
  1087. public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
  1088. {
  1089. occupation.setLevelFour(newValue);
  1090. }});
  1091. comboContainer.getChildren().add(levelOne);
  1092. comboContainer.getChildren().add(levelTwo);
  1093. comboContainer.getChildren().add(levelThree);
  1094. comboContainer.getChildren().add(levelFour);
  1095. HBox jobInfo = new HBox();
  1096. jobInfo.setId("jobInfo");
  1097. jobInfo.minWidthProperty()
  1098. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1099. jobInfo.maxWidthProperty()
  1100. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1101. jobInfo.maxHeightProperty()
  1102. .bind(Bindings.createDoubleBinding(() -> jobInfo.getPrefHeight(), jobInfo.prefHeightProperty()));
  1103. comboContainer.minHeightProperty()
  1104. .bind(Bindings.createDoubleBinding(() -> addJob.getHeight(), addJob.heightProperty()));
  1105. addJob.maxWidthProperty()
  1106. .bind(Bindings.createDoubleBinding(() -> container.getWidth()/2, container.widthProperty()));
  1107. HBox.setHgrow(comboContainer,Priority.ALWAYS);
  1108. jobInfo.getChildren().add(comboContainer);
  1109. jobInfo.getChildren().add(addJob);
  1110. // VBox occupationSelection = new VBox();
  1111. // occupationSelection.setBorder(Utils.darkBlueBorder);
  1112. // occupationSelection.getChildren().add(jobInfo);
  1113. // occupationSelection.getChildren().add(new OccupationSelectionView(1, getOccupationList("")));
  1114. return jobInfo;
  1115. }
  1116. /**
  1117. * The health data file is read and the overview of all found data is included
  1118. * in the datalist
  1119. *
  1120. * @throws WrappedException if the file coulnd't be read
  1121. */
  1122. public void fillDataList() throws WrappedException
  1123. {
  1124. datalist.getChildren().clear();
  1125. File file;
  1126. if (filename.contains(".zip"))
  1127. { // a zip file can't be read just like this and so the containing file needs to
  1128. // be copied into temp
  1129. try
  1130. {
  1131. ZipFile f = new ZipFile(filename);
  1132. String xmlFileName = "apple_health_export/Export.xml";
  1133. file = Files.createTempFile("health_data", "__Export.xml").toFile();
  1134. file.deleteOnExit();
  1135. ZipEntry xml = f.getEntry(xmlFileName);
  1136. if (xml == null)
  1137. {
  1138. xml = f.getEntry(xmlFileName.toLowerCase());
  1139. }
  1140. try (BufferedInputStream is = new BufferedInputStream(f.getInputStream(xml));
  1141. BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)))
  1142. {
  1143. byte[] b = new byte[1024];
  1144. int res = is.read(b);
  1145. while (res > -1)
  1146. {
  1147. out.write(b, 0, res);
  1148. res = is.read(b);
  1149. }
  1150. }
  1151. f.close();
  1152. }
  1153. catch (Exception e)
  1154. {
  1155. // The zip-File couldn't be read, maybe it was the wrong file or error
  1156. throw new WrappedException(e, Text.E_READ_ZIP_FILE);
  1157. }
  1158. }
  1159. else
  1160. {
  1161. file = new File(filename);
  1162. }
  1163. try
  1164. {
  1165. worker = new ParsingWrapper(file);
  1166. }
  1167. catch (Exception e)
  1168. {
  1169. throw new WrappedException(e, Text.E_READ_XML_FILE);
  1170. }
  1171. ReadHandler read = worker.getRead();
  1172. // an EntryView is created per EntryType (ME)
  1173. for (EntryType type : EntryType.ALL_ME_ONES)
  1174. {
  1175. EntryView ev;
  1176. if (type.equals(EntryType.EXPORT_DATE))
  1177. {
  1178. ev = new EntryView(new Element(type, read.getExportDate()));
  1179. }
  1180. else if (type.equals(EntryType.REGION_CODE))
  1181. {
  1182. ev = new EntryView(new Element(type, read.getRegionCode()));
  1183. }
  1184. else if (type == EntryType.DATE_OF_BIRTH)
  1185. {
  1186. String value = read.getDateOfBirth();
  1187. if (value == null || value.equals("") || value.equals(Text.NOT_SET))
  1188. { // Don't add an element when there is no data given
  1189. continue;
  1190. }
  1191. ev = new EntryView(new Element(type, read.getDateOfBirth()), read.getSplitDateOfBirth());
  1192. }
  1193. else
  1194. {
  1195. String value = read.getMeInfo(type);
  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, value));
  1201. }
  1202. ev.maxWidthProperty()
  1203. .bind(Bindings.createDoubleBinding(
  1204. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1205. datalist.widthProperty()));
  1206. ev.minWidthProperty()
  1207. .bind(Bindings.createDoubleBinding(
  1208. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1209. datalist.widthProperty()));
  1210. datalist.getChildren().add(ev);
  1211. datalist.getChildren().add(getSeparator(datalist));
  1212. ev.addCheckListener(this);
  1213. }
  1214. // an EntryView is created per EntryType (not ME)
  1215. for (EntryType type : EntryType.ALL_SEPARATE_ONES)
  1216. {
  1217. int n = read.getTypeNumber(type);
  1218. if (n > 0)
  1219. {
  1220. EntryView ev;
  1221. if (type == EntryType.WORKOUT)
  1222. {
  1223. ev = new EntryView(new Element(type, n + ""), read.getSubWorkouts());
  1224. }
  1225. else if (type == EntryType.RECORD)
  1226. {
  1227. ev = new EntryView(new Element(type, n + ""), read.getSubRecords());
  1228. }
  1229. else
  1230. {
  1231. ev = new EntryView(new Element(type, n + ""));
  1232. }
  1233. ev.addCheckListener(this);
  1234. ev.maxWidthProperty()
  1235. .bind(Bindings.createDoubleBinding(
  1236. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1237. datalist.widthProperty()));
  1238. ev.minWidthProperty()
  1239. .bind(Bindings.createDoubleBinding(
  1240. () -> datalist.getWidth() - datalist.getBorder().getInsets().getRight() * 2,
  1241. datalist.widthProperty()));
  1242. datalist.getChildren().add(ev);
  1243. datalist.getChildren().add(getSeparator(datalist));
  1244. }
  1245. }
  1246. if (datalist.getChildren().size() > 0) // something inside, so there is a separator too much
  1247. {
  1248. datalist.getChildren().remove(datalist.getChildren().size() - 1);
  1249. }
  1250. }
  1251. /**
  1252. * Used to display in the overview when the initial health file couldn't be
  1253. * read. Consists of a BorderPane with a text in its center
  1254. *
  1255. * @param text Text to be displayed
  1256. * @return the created BorderPane
  1257. */
  1258. private BorderPane getEmptyFileView(String text)
  1259. {
  1260. BorderPane p = new BorderPane();
  1261. p.setCenter(new Label(text));
  1262. return p;
  1263. }
  1264. /**
  1265. * In case there was an error reading the inital health file, the overview will
  1266. * only show an information text regarding the error
  1267. *
  1268. * @param text Text to be displayed
  1269. */
  1270. public void setErrorReadingFile(String text)
  1271. {
  1272. datalist.getChildren().add(getEmptyFileView(text));
  1273. }
  1274. /**
  1275. * Saves the error which occurred when the temp files are created
  1276. *
  1277. * @param text Details on why it didn't work
  1278. */
  1279. public void setErrorWritingTempFiles(String text)
  1280. {
  1281. filesInTempWithDesc = null;
  1282. errorWritingFiles = text;
  1283. }
  1284. /**
  1285. * Creates a horizontal separator line to be used in lists
  1286. *
  1287. * @param toBind The view it should be bound to (length wise)
  1288. * @return The created separator
  1289. */
  1290. private BorderPane getSeparator(Pane toBind)
  1291. {
  1292. BorderPane p = new BorderPane();
  1293. Line line = new Line();
  1294. line.setStartX(0);
  1295. line.setStartY(0);
  1296. line.setEndY(0);
  1297. line.endXProperty().bind(Bindings.createDoubleBinding(
  1298. () -> toBind.getWidth() - toBind.getBorder().getInsets().getRight() * 2, toBind.widthProperty()));
  1299. line.setStroke(Colors.darkGrey);
  1300. p.setCenter(line);
  1301. return p;
  1302. }
  1303. /**
  1304. * Creates the last view showing the overview of all saved data and the help to
  1305. * upload the created zip file
  1306. **/
  1307. public VBox getResultView()
  1308. {
  1309. VBox container = new VBox();
  1310. container.setId("container");
  1311. Label done = new Label(Text.SUCCESS);
  1312. container.getChildren().add(done);
  1313. // buttons for easier access to nextcloud
  1314. HBox nextcloudHelp = new HBox();
  1315. nextcloudHelp.setId("nextcloudHelp");
  1316. nextcloudHelp.minWidthProperty()
  1317. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1318. // open in browser
  1319. BorderPane goToNextcloud = new BorderPane();
  1320. goToNextcloud.setId("goToNextcloud");
  1321. Label nextcloudText = new Label(Text.NEXTCLOUD);
  1322. goToNextcloud.setCenter(nextcloudText);
  1323. goToNextcloud.setLeft(new ImageView(
  1324. new Image(this.getClass().
  1325. getResourceAsStream("internet.png"),64.0,64.0, true, true)));
  1326. goToNextcloud.setOnMouseClicked(new EventHandler<MouseEvent>()
  1327. { // open in browser if possible
  1328. @Override
  1329. public void handle(MouseEvent arg0)
  1330. {
  1331. try
  1332. {
  1333. hostServices.showDocument(new URL(Text.NEXTCLOUD_ADDRESS).toExternalForm());
  1334. }
  1335. catch (MalformedURLException e)
  1336. { // otherwise do nothing
  1337. }
  1338. }
  1339. });
  1340. nextcloudHelp.getChildren().add(goToNextcloud);
  1341. List<File> clipboardFiles = new ArrayList<>();
  1342. clipboardFiles.add(fileForSaving);
  1343. // copy filepath to clipboard
  1344. BorderPane showInFileBrowser = new BorderPane();
  1345. showInFileBrowser.setId("openFileInFileBrowser");
  1346. Label openInFileBrowserText = new Label(Text.OPEN_FILE_IN_BROWSER);
  1347. showInFileBrowser.setCenter(openInFileBrowserText);
  1348. showInFileBrowser.setLeft(new ImageView(
  1349. new Image(this.getClass().
  1350. getResourceAsStream("filepath.png"),64.0,64.0, true, true)));
  1351. showInFileBrowser.setOnMouseClicked(new EventHandler<MouseEvent>()
  1352. {
  1353. @Override
  1354. public void handle(MouseEvent arg0)
  1355. {
  1356. hostServices.showDocument(fileForSaving.getParent());
  1357. }
  1358. });
  1359. // drag file to browser
  1360. BorderPane dragToNextcloud = new BorderPane();
  1361. dragToNextcloud.setId("dragToServer");
  1362. dragToNextcloud.setCenter(new Label(Text.DRAG_FILE));
  1363. dragToNextcloud.setLeft(new ImageView(
  1364. new Image(this.getClass().
  1365. getResourceAsStream("archive.png"),64.0,64.0, true, true)));
  1366. dragToNextcloud.setOnMouseEntered(new EventHandler<MouseEvent>()
  1367. {
  1368. @Override
  1369. public void handle(MouseEvent event)
  1370. {
  1371. scene.setCursor(Cursor.OPEN_HAND);
  1372. }
  1373. });
  1374. dragToNextcloud.setOnMouseExited(new EventHandler<MouseEvent>()
  1375. {
  1376. @Override
  1377. public void handle(MouseEvent event)
  1378. {
  1379. scene.setCursor(Cursor.DEFAULT);
  1380. }
  1381. });
  1382. dragToNextcloud.setOnDragDetected(new EventHandler<MouseEvent>()
  1383. {
  1384. @Override
  1385. public void handle(MouseEvent event)
  1386. {
  1387. /* drag was detected, start a drag-and-drop gesture */
  1388. /* allow any transfer mode */
  1389. Dragboard db = dragToNextcloud.startDragAndDrop(TransferMode.ANY);
  1390. /* Put a string on a dragboard */
  1391. ClipboardContent content = new ClipboardContent();
  1392. content.put(DataFormat.FILES, clipboardFiles);
  1393. // content.putString(source.getText());
  1394. db.setContent(content);
  1395. event.consume();
  1396. }
  1397. });
  1398. HBox.setHgrow(showInFileBrowser, Priority.ALWAYS);
  1399. HBox.setHgrow(dragToNextcloud, Priority.ALWAYS);
  1400. HBox.setHgrow(goToNextcloud, Priority.ALWAYS);
  1401. nextcloudHelp.getChildren().add(dragToNextcloud);
  1402. nextcloudHelp.getChildren().add(showInFileBrowser);
  1403. container.getChildren().add(nextcloudHelp);
  1404. TextArea copyable = new TextArea(String.format(Text.F_RAW_UPLOAD_DATA, fileForSaving.getAbsolutePath(),Text.NEXTCLOUD_ADDRESS));
  1405. copyable.setEditable(false);
  1406. copyable.setWrapText(true);
  1407. copyable.setId("buttonProblems");
  1408. copyable.setFocusTraversable(false);
  1409. copyable.setPrefHeight(70);
  1410. container.getChildren().add(copyable);
  1411. // table to show all saved data
  1412. VBox overview = new VBox();
  1413. overview.minWidthProperty()
  1414. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1415. container.getChildren().add(overview);
  1416. overview.setId("savedOverview");
  1417. overview.setBorder(Utils.darkBlueBorder);
  1418. // header
  1419. Label savedOverview = new Label(Text.DATA_OVERVIEW);
  1420. savedOverview.minWidthProperty()
  1421. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1422. overview.getChildren().add(savedOverview);
  1423. savedOverview.setStyle("-fx-font-weight: bold");
  1424. // add job
  1425. if (!occupation.getLevelOne().equals(Text.NOT_SET))
  1426. {
  1427. Label x = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1428. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_ONE, occupation.getLevelOne()));
  1429. x.minWidthProperty()
  1430. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1431. overview.getChildren().add(x);
  1432. overview.getChildren().add(getSeparator(overview));
  1433. if (!occupation.getLevelTwo().equals(Text.NOT_SET))
  1434. {
  1435. Label x2 = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1436. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_TWO, occupation.getLevelTwo()));
  1437. x2.minWidthProperty()
  1438. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1439. overview.getChildren().add(x2);
  1440. overview.getChildren().add(getSeparator(overview));
  1441. if (!occupation.getLevelThree().equals(Text.NOT_SET))
  1442. {
  1443. Label x3 = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1444. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_THREE, occupation.getLevelThree()));
  1445. x3.minWidthProperty()
  1446. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1447. overview.getChildren().add(x3);
  1448. overview.getChildren().add(getSeparator(overview));
  1449. if (!occupation.getLevelFour().equals(Text.NOT_SET))
  1450. {
  1451. Label x4 = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY,
  1452. EntryType.JOB.getValue(), Text.OCCUPATION_LEVEL_FOUR, occupation.getLevelFour()));
  1453. x4.minWidthProperty()
  1454. .bind(Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1455. overview.getChildren().add(x4);
  1456. overview.getChildren().add(getSeparator(overview));
  1457. }
  1458. }
  1459. }
  1460. }
  1461. // add all selected Data
  1462. for (Node n : datalist.getChildren())
  1463. {
  1464. if (n instanceof EntryView) // this should always be true
  1465. {
  1466. EntryView i = (EntryView) n;
  1467. EntryType type = i.getEntryType();
  1468. if (i.getSelected())
  1469. {
  1470. if (type.equals(EntryType.WORKOUT) || type.equals(EntryType.RECORD)
  1471. || type.equals(EntryType.DATE_OF_BIRTH))
  1472. { // selected ones with subelements
  1473. for (SubEntryView s : i.getSelectedSubElements())
  1474. {
  1475. Label x = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY_ENTRIES,
  1476. type.getValue(), Utils.shortenHKStrings(s.getName()), s.getValue()));
  1477. if (type.equals(EntryType.DATE_OF_BIRTH))
  1478. {
  1479. x = new Label(String.format(Text.F_SAVED_DATA_VALUE_SUBCATEGORY, type.getValue(),
  1480. s.getName(), s.getValue()));
  1481. }
  1482. x.minWidthProperty().bind(
  1483. Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1484. overview.getChildren().add(x);
  1485. overview.getChildren().add(getSeparator(overview));
  1486. }
  1487. }
  1488. else
  1489. { // selected ones without subelements
  1490. String value = i.getValue();
  1491. Label x = new Label(String.format(Text.F_SAVED_DATA_VALUE, type.getValue(), value));
  1492. if (type.equals(EntryType.ACTIVITY_SUMMARY))
  1493. {
  1494. x = new Label(String.format(Text.F_SAVED_DATA_ENTRIES, type.getValue(), value));
  1495. }
  1496. x.minWidthProperty().bind(
  1497. Bindings.createDoubleBinding(() -> overview.getWidth(), overview.widthProperty()));
  1498. overview.getChildren().add(x);
  1499. overview.getChildren().add(getSeparator(overview));
  1500. }
  1501. }
  1502. }
  1503. }
  1504. return container;
  1505. }
  1506. /**
  1507. * creates the view to show the privacy policy
  1508. *
  1509. * @return
  1510. */
  1511. public VBox getPrivacyView()
  1512. {
  1513. VBox container = new VBox();
  1514. container.setId("container");
  1515. container.getChildren().add(getHeaderLabel(PrivacyStatementText.heading,22));
  1516. container.getChildren().add(getContentLabel(PrivacyStatementText.responsibleParty, container));
  1517. container.getChildren().add(getContentLabel(PrivacyStatementText.introText, container));
  1518. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec1, 16));
  1519. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec1, container));
  1520. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec2, 16));
  1521. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec2, container));
  1522. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec3, 16));
  1523. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec3, container));
  1524. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec4, 16));
  1525. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec4, container));
  1526. container.getChildren().add(getHeaderLabel(PrivacyStatementText.headingSec5, 16));
  1527. container.getChildren().add(getContentLabel(PrivacyStatementText.contentSec5, container));
  1528. return container;
  1529. }
  1530. private Label getHeaderLabel(String text, int size)
  1531. {
  1532. Label res = new Label(text);
  1533. res.setStyle("-fx-font-weight: bold; -fx-font-size: "+size+"px;");
  1534. return res;
  1535. }
  1536. /**
  1537. * Creates a label for the Privacy view which is used for normal text content. Fills the whole width of its parent
  1538. * @param text Text to be displayed
  1539. * @param container Parent to set the width
  1540. * @return created label
  1541. */
  1542. private Label getContentLabel(String text, Pane container)
  1543. {
  1544. Label res = new Label(text);
  1545. res.minWidthProperty()
  1546. .bind(Bindings.createDoubleBinding(() -> container.getWidth(), container.widthProperty()));
  1547. return res;
  1548. }
  1549. /**
  1550. * Creates the files in the temp directory
  1551. *
  1552. * @throws WrappedException
  1553. */
  1554. public void createTempFiles() throws WrappedException
  1555. {
  1556. filesInTempWithDesc = worker.writeSelectedDataToFiles(new WriteSelection(datalist), occupation);
  1557. }
  1558. @Override
  1559. public void changed(ObservableValue<? extends Boolean> checkbox, Boolean oldValue, Boolean newValue)
  1560. {
  1561. if (newValue)
  1562. {
  1563. selectCount++;
  1564. }
  1565. else
  1566. {
  1567. selectCount--;
  1568. }
  1569. if (selectCount > 0 && dirForSaving != null)
  1570. {
  1571. updateNav(NavState.DATA_AND_PATH_SELECTED);
  1572. }
  1573. else
  1574. {
  1575. updateNav(NavState.SELECT_DATA);
  1576. }
  1577. }
  1578. }