org.openbase.display.DisplayView.java Source code

Java tutorial

Introduction

Here is the source code for org.openbase.display.DisplayView.java

Source

package org.openbase.display;

/*
 * #%L
 * GenericDisplay
 * %%
 * Copyright (C) 2015 - 2016 openbase.org
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.apache.commons.lang.builder.HashCodeBuilder;
import static org.openbase.display.DisplayRemoteSend.handleProperties;
import org.openbase.display.jp.JPBroadcastDisplayScope;
import org.openbase.display.jp.JPDisplayScope;
import org.openbase.display.jp.JPImageUrl;
import org.openbase.display.jp.JPMessage;
import org.openbase.display.jp.JPMessageType;
import org.openbase.display.jp.JPOutput;
import org.openbase.display.jp.JPTabAmount;
import org.openbase.display.jp.JPUrl;
import org.openbase.display.jp.JPVisible;
import org.openbase.jps.core.JPService;
import org.openbase.jps.exception.JPServiceException;
import org.openbase.jul.exception.CouldNotPerformException;
import org.openbase.jul.exception.InitializationException;
import org.openbase.jul.exception.InstantiationException;
import org.openbase.jul.exception.NotAvailableException;
import org.openbase.jul.exception.printer.ExceptionPrinter;
import org.slf4j.LoggerFactory;

/**
 *
 * @author <a href="mailto:divine@openbase.org">Divine Threepwood</a>
 */
public class DisplayView extends Application implements Display {

    protected static final org.slf4j.Logger logger = LoggerFactory.getLogger(DisplayView.class);

    public static final int AMOUNT_OF_TAB_FALLBACK = 10;

    private DisplayServer broadcastServer, displayServer;
    private Stage primaryStage;
    private final int maxTabAmount;

    private final HashMap<Integer, WebTab> webTabMap;
    private final ConcurrentLinkedQueue<WebTab> webTabUsageQueue;
    private final HTMLLoader htmlLoader;
    private final Pane cardsPane;

    public DisplayView() throws InstantiationException {
        try {
            this.webTabMap = new HashMap<>();
            this.webTabUsageQueue = new ConcurrentLinkedQueue<>();
            this.htmlLoader = new HTMLLoader();
            this.cardsPane = new StackPane();

            int tmpMaxTabAmount;
            try {
                tmpMaxTabAmount = JPService.getProperty(JPTabAmount.class).getValue();
            } catch (JPServiceException ex) {
                tmpMaxTabAmount = AMOUNT_OF_TAB_FALLBACK;
            }
            this.maxTabAmount = tmpMaxTabAmount;
        } catch (CouldNotPerformException ex) {
            throw new InstantiationException(this, ex);
        }
    }

    private void init(final Stage primaryStage) throws InterruptedException, InitializationException {

        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                Platform.exit();
            }
        });

        try {
            // platform configuration
            Platform.setImplicitExit(false);
            primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
            this.primaryStage = primaryStage;

            Scene scene = new Scene(cardsPane);

            // configure hide key combination
            final KeyCombination escapeKey = new KeyCodeCombination(KeyCode.ESCAPE);
            scene.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {

                @Override
                public void handle(KeyEvent event) {
                    if (escapeKey.match(event)) {
                        try {
                            setVisible(false);
                        } catch (CouldNotPerformException ex) {
                            ExceptionPrinter.printHistory(
                                    new CouldNotPerformException("Could not execute key event!", ex), logger);
                        }
                    }
                }
            });

            primaryStage.setScene(scene);

            try {
                broadcastServer = new DisplayServer(this);
                broadcastServer.init(JPService.getProperty(JPBroadcastDisplayScope.class).getValue());
                broadcastServer.activate();
            } catch (JPServiceException | CouldNotPerformException ex) {
                throw new CouldNotPerformException("Could not load display server!", ex);
            }

            try {
                displayServer = new DisplayServer(this);
                displayServer.init(JPService.getProperty(JPDisplayScope.class).getValue());
                displayServer.activate();
            } catch (JPServiceException | CouldNotPerformException ex) {
                throw new CouldNotPerformException("Could not load display server!", ex);
            }
            this.htmlLoader.init(getScreen());
        } catch (CouldNotPerformException ex) {
            throw new InitializationException(this, ex);
        }
    }

    @Override
    public void start(final Stage primaryStage) throws Exception {
        try {
            init(primaryStage);
            showText(" ");

            if (JPService.getProperty(JPVisible.class).getValue()) {
                setVisible(true);
            }

        } catch (CouldNotPerformException ex) {
            throw ExceptionPrinter.printHistoryAndReturnThrowable(
                    new CouldNotPerformException("Could not start gui!", ex), logger);
        }
        new Thread() {
            @Override
            public void run() {
                try {
                    handleProperties(false);
                } catch (CouldNotPerformException | InterruptedException ex) {
                    ExceptionPrinter.printHistory(ex, logger);
                }
            }
        }.start();
    }

    @Override
    public void stop() throws Exception {
        super.stop();
        displayServer.shutdown();
        broadcastServer.shutdown();
        webTabMap.values().forEach(tab -> {
            tab.shutdown();
        });
    }

    private int getHash(final String context) {
        return new HashCodeBuilder().append(context).toHashCode();
    }

    private WebTab loadWebEngine(final String context) {
        assert getHash(context) == getHash(context);
        int contextHash = getHash(context);
        WebTab webTab;

        if (!webTabMap.containsKey(contextHash)) {
            if (webTabMap.size() >= maxTabAmount) {

                webTab = webTabUsageQueue.poll();
                // update context hash
                webTabMap.remove(webTab.getContentHash());
                webTab.updateContextHash(contextHash);

                webTabMap.put(contextHash, webTab);
            } else {
                webTabMap.put(contextHash, new WebTab(contextHash, cardsPane));
            }
        }

        // add tab to queue tail and return web engine.
        webTab = webTabMap.get(contextHash);
        if (webTabUsageQueue.contains(webTab)) {
            webTabUsageQueue.remove(webTab);
        }
        webTabUsageQueue.offer(webTab);
        return webTab;
    }

    private Future<Void> displayHTML(final String html, boolean show) throws CouldNotPerformException {
        return runTask(() -> {
            loadWebEngine(html).loadContent(html);
            if (show) {
                setVisible(show);
            }
            return null;
        });
    }

    private Future<Void> displayURL(final String url, boolean show) throws CouldNotPerformException {
        return runTask(() -> {
            loadWebEngine(url).load(url);
            if (show) {
                setVisible(show);
            }
            return null;
        });
    }

    /**
     * {@inheritDoc}
     *
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> showURL(final String url) throws CouldNotPerformException {
        return displayURL(url, false);
    }

    /**
     * {@inheritDoc}
     *
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> showHTMLContent(final String content) throws CouldNotPerformException {
        return displayHTML(content, true);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> showInfoText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.FORESTGREEN.darker()), true);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> showWarnText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.ORANGE), true);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> showErrorText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.RED.darker()), true);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> showText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.BLACK), true);
    }

    /**
     * {@inheritDoc}
     *
     * @param image
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> showImage(final String image) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadImageView(image), true);
    }

    /**
     * {@inheritDoc}
     *
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setURL(final String url) throws CouldNotPerformException {
        return displayURL(url, false);
    }

    /**
     * {@inheritDoc}
     *
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setHTMLContent(final String content) throws CouldNotPerformException {
        return displayHTML(content, false);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setInfoText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.FORESTGREEN.darker()), false);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setWarnText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.ORANGE), false);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setErrorText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.RED.darker()), false);
    }

    /**
     * {@inheritDoc}
     *
     * @param presetId
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setText(final String presetId) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadTextView(presetId, Color.BLACK), false);
    }

    /**
     * {@inheritDoc}
     *
     * @param image
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setImage(final String image) throws CouldNotPerformException {
        return displayHTML(htmlLoader.loadImageView(image), false);
    }

    /**
     * {@inheritDoc}
     *
     * @param visible
     * @throws org.openbase.jul.exception.CouldNotPerformException
     */
    @Override
    public Future<Void> setVisible(final Boolean visible) throws CouldNotPerformException {
        return runTask(() -> {
            if (visible) {
                Screen screen = getScreen();
                Stage stage = getStage();
                stage.setX(screen.getVisualBounds().getMinX());
                stage.setY(screen.getVisualBounds().getMinY());
                stage.setHeight(screen.getVisualBounds().getHeight());
                stage.setWidth(screen.getVisualBounds().getWidth());
                getStage().setFullScreen(false);
                getStage().setAlwaysOnTop(true);
                getStage().setFullScreen(true);
                getStage().show();

            } else {
                getStage().hide();
                getStage().setFullScreen(false);
            }
            return null;
        });
    }

    private Screen getScreen() {
        try {

            // setup display property
            JPOutput.Display display;
            try {
                display = JPService.getProperty(JPOutput.class).getValue();
            } catch (JPServiceException ex) {
                ExceptionPrinter.printHistory(new CouldNotPerformException(
                        "Could not detect output property! Try to use default display.", ex), logger);
                display = JPOutput.Display.PRIMARY;
            }

            // select screen
            switch (display) {
            case PRIMARY:
                return Screen.getPrimary();
            case SECONDARY:
                for (Screen screen : Screen.getScreens()) {
                    if (!screen.equals(Screen.getPrimary())) {
                        return screen;
                    }
                }
                throw new NotAvailableException(Screen.class, JPOutput.Display.SECONDARY.name());
            default:
                return Screen.getScreens().get(display.getId());
            }
        } catch (Exception ex) {
            ExceptionPrinter.printHistory(
                    new CouldNotPerformException("Could not detect display! Use display 0 instead.", ex), logger);
            return Screen.getScreens().get(0);
        }
    }

    private Stage getStage() throws NotAvailableException {
        if (primaryStage == null) {
            throw new NotAvailableException(Stage.class);
        }
        return primaryStage;
    }

    private <V> Future<V> runTask(final Callable<V> callable) throws CouldNotPerformException {
        try {

            if (Platform.isFxApplicationThread()) {
                try {
                    return CompletableFuture.completedFuture(callable.call());
                } catch (Exception ex) {
                    ExceptionPrinter.printHistory(new CouldNotPerformException("Could not perform task!", ex),
                            logger);
                }
            }

            FutureTask<V> future = new FutureTask(() -> {
                try {
                    return callable.call();
                } catch (Exception ex) {
                    throw ExceptionPrinter.printHistoryAndReturnThrowable(ex, logger);
                }
            });
            Platform.runLater(future);
            return future;
        } catch (Exception ex) {
            throw new CouldNotPerformException("Could not perform task!", ex);
        }
    }

    public static void main(String[] args) {

        // Configure and parse command line properties
        JPService.setApplicationName("generic-display");
        JPService.registerProperty(JPBroadcastDisplayScope.class);
        JPService.registerProperty(JPDisplayScope.class);
        JPService.registerProperty(JPOutput.class);
        JPService.registerProperty(JPMessage.class);
        JPService.registerProperty(JPTabAmount.class);
        JPService.registerProperty(JPUrl.class);
        JPService.registerProperty(JPImageUrl.class);
        JPService.registerProperty(JPVisible.class);
        JPService.registerProperty(JPMessageType.class);
        JPService.parseAndExitOnError(args);

        // launch user interface
        launch(args);
    }
}