protected JPopupMenu buildSystemTrayJPopupMenu(Stage primaryStage) throws ClassNotFoundException, UnsupportedLookAndFeelException, InstantiationException, IllegalAccessException { final JPopupMenu menu = new JPopupMenu(); final JMenuItem showMenuItem = new JMenuItem("Show"); final JMenuItem exitMenuItem = new JMenuItem("Exit"); menu.add(showMenuItem); menu.addSeparator(); menu.add(exitMenuItem); showMenuItem.addActionListener(ae -> Platform.runLater(primaryStage::show)); exitMenuItem.addActionListener(ae -> System.exit(0)); return menu; }
public class DesktopClient extends Application { private static final Logger logger = LoggerFactory.getLogger(DesktopClient.class.getSimpleName()); private static final String TITLE = "Qabel Desktop Client"; private static Path DATABASE_FILE = Paths.get(System.getProperty("user.home")).resolve(".qabel/db.sqlite"); private final Map<String, Object> customProperties = new HashMap<>(); private boolean inBound; private LayoutView view; private HttpDropConnector dropConnector = new HttpDropConnector(); private PersistenceDropMessageRepository dropMessageRepository; private PersistenceContactRepository contactRepository; private BoxVolumeFactory boxVolumeFactory; private Stage primaryStage; private MonitoredTransferManager transferManager; private MessageRendererFactory rendererFactory = new MessageRendererFactory(); private BlockSharingService sharingService; private ExecutorService executorService = Executors.newCachedThreadPool(); private ClientConfiguration config; private boolean visible = false; private PersistenceIdentityRepository identityRepository; public static void main(String[] args) throws Exception { if (args.length > 0) { DATABASE_FILE = Paths.get(args[0]); } UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); launch(args); } @Override public void start(Stage stage) throws Exception { primaryStage = stage; setUserAgentStylesheet(STYLESHEET_MODENA); config = initDiContainer(); SceneAntialiasing aa = SceneAntialiasing.BALANCED; primaryStage .getIcons() .setAll( new javafx.scene.image.Image(getClass().getResourceAsStream("/logo-invert_small.png"))); Scene scene; Platform.setImplicitExit(false); primaryStage.setTitle(TITLE); scene = new Scene(new LoginView().getView(), 370, 550, true, aa); primaryStage.setScene(scene); config.addObserver( (o, arg) -> { Platform.runLater( () -> { if (arg instanceof Account) { try { ClientConfiguration configuration = (ClientConfiguration) customProperties.get("clientConfiguration"); Account acc = (Account) arg; AccountingServer server = new AccountingServer( new URI(acc.getProvider()), acc.getUser(), acc.getAuth()); AccountingHTTP accountingHTTP = new AccountingHTTP(server, new AccountingProfile()); BoxVolumeFactory factory = new BlockBoxVolumeFactory( configuration.getDeviceId().getBytes(), accountingHTTP, identityRepository); boxVolumeFactory = new CachedBoxVolumeFactory(factory); customProperties.put("boxVolumeFactory", boxVolumeFactory); sharingService = new BlockSharingService(dropMessageRepository, dropConnector); customProperties.put("sharingService", sharingService); new Thread(getSyncDaemon(config)).start(); new Thread(getDropDaemon(config)).start(); view = new LayoutView(); Parent view = this.view.getView(); Scene layoutScene = new Scene(view, 800, 600, true, aa); Platform.runLater(() -> primaryStage.setScene(layoutScene)); if (config.getSelectedIdentity() != null) { addShareMessageRenderer(config.getSelectedIdentity()); } } catch (Exception e) { logger.error("failed to init background services: " + e.getMessage(), e); // TODO to something with the fault } } else if (arg instanceof Identity) { addShareMessageRenderer((Identity) arg); } }); }); dropMessageRepository.addObserver(new ShareNotificationHandler(config)); setTrayIcon(primaryStage); Runtime.getRuntime() .addShutdownHook( new Thread() { public void run() { Platform.exit(); } }); primaryStage.show(); } private void addShareMessageRenderer(Identity arg) { executorService.submit( () -> { ShareNotificationRenderer renderer = new ShareNotificationRenderer( ((BoxVolumeFactory) customProperties.get("boxVolumeFactory")) .getVolume(config.getAccount(), arg) .getReadBackend(), sharingService); rendererFactory.addRenderer( DropMessageRepository.PAYLOAD_TYPE_SHARE_NOTIFICATION, renderer); }); } protected SyncDaemon getSyncDaemon(ClientConfiguration config) { new Thread(transferManager, "TransactionManager").start(); return new SyncDaemon( config.getBoxSyncConfigs(), new DefaultSyncerFactory(boxVolumeFactory, transferManager)); } protected DropDaemon getDropDaemon(ClientConfiguration config) throws PersistenceException, EntityNotFoundExcepion { return new DropDaemon(config, dropConnector, contactRepository, dropMessageRepository); } private ClientConfiguration initDiContainer() throws Exception { if (!Files.exists(DATABASE_FILE) && !Files.exists(DATABASE_FILE.getParent())) { Files.createDirectories(DATABASE_FILE.getParent()); } Persistence<String> persistence = new SQLitePersistence(DATABASE_FILE.toFile().getAbsolutePath()); transferManager = new MonitoredTransferManager(new DefaultTransferManager()); customProperties.put("loadManager", transferManager); customProperties.put("transferManager", transferManager); customProperties.put("persistence", persistence); customProperties.put("dropUrlGenerator", new DropUrlGenerator("https://qdrop.prae.me")); identityRepository = new PersistenceIdentityRepository(persistence); customProperties.put("identityRepository", identityRepository); PersistenceAccountRepository accountRepository = new PersistenceAccountRepository(persistence); customProperties.put("accountRepository", accountRepository); contactRepository = new PersistenceContactRepository(persistence); customProperties.put("contactRepository", contactRepository); dropMessageRepository = new PersistenceDropMessageRepository(persistence); customProperties.put("dropMessageRepository", dropMessageRepository); customProperties.put("dropConnector", dropConnector); customProperties.put("reportHandler", new HockeyApp()); ClientConfiguration clientConfig = getClientConfiguration(persistence, identityRepository, accountRepository); if (!clientConfig.hasDeviceId()) { clientConfig.setDeviceId(generateDeviceId()); } PersistenceContactRepository contactRepository = new PersistenceContactRepository(persistence); customProperties.put("contactRepository", contactRepository); customProperties.put("clientConfiguration", clientConfig); customProperties.put("primaryStage", primaryStage); rendererFactory.setFallbackRenderer(new PlaintextMessageRenderer()); customProperties.put("messageRendererFactory", rendererFactory); Injector.setConfigurationSource(customProperties::get); Injector.setInstanceSupplier(new RecursiveInjectionInstanceSupplier(customProperties)); return clientConfig; } private String generateDeviceId() { return UUID.randomUUID().toString(); } private ClientConfiguration getClientConfiguration( Persistence<String> persistence, IdentityRepository identityRepository, AccountRepository accountRepository) { ClientConfigurationRepository repo = new PersistenceClientConfigurationRepository( persistence, new ClientConfigurationFactory(), identityRepository, accountRepository); final ClientConfiguration config = repo.load(); config.addObserver((o, arg) -> repo.save(config)); return config; } private void setTrayIcon(Stage primaryStage) throws ClassNotFoundException, UnsupportedLookAndFeelException, InstantiationException, IllegalAccessException { if (!SystemTray.isSupported()) { return; } SystemTray sTray = SystemTray.getSystemTray(); primaryStage.setOnCloseRequest(arg0 -> primaryStage.hide()); JPopupMenu popup = buildSystemTrayJPopupMenu(primaryStage); URL url = System.class.getResource("/logo-invert_small.png"); Image img = Toolkit.getDefaultToolkit().getImage(url); TrayIcon icon = new TrayIcon(img, "Qabel"); icon.setImageAutoSize(true); trayIconListener(popup, icon); try { sTray.add(icon); } catch (AWTException e) { logger.error("failed to add tray icon: " + e.getMessage(), e); } } private void trayIconListener(final JPopupMenu popup, TrayIcon icon) { Timer notificationTimer = new Timer(); notificationTimer.schedule( new TimerTask() { @Override public void run() { if (visible && !inBound) { visible = !visible; popup.setVisible(visible); } inBound = false; } }, 250, 1500); icon.addMouseMotionListener( new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { inBound = true; } }); popup.addMouseMotionListener( new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { inBound = true; } }); icon.addMouseListener( new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { if (e.getButton() != MouseEvent.BUTTON1) { return; } Point point = e.getPoint(); Rectangle bounds = getScreenViewableBounds(getGraphicsDeviceAt(point)); int x = point.x; int y = point.y; if (y < bounds.y) { y = bounds.y; } else if (y > bounds.y + bounds.height) { y = bounds.y + bounds.height; } if (x < bounds.x) { x = bounds.x; } else if (x > bounds.x + bounds.width) { x = bounds.x + bounds.width; } if (x + popup.getWidth() > bounds.x + bounds.width) { x = (bounds.x + bounds.width) - popup.getWidth(); } if (y + popup.getWidth() > bounds.y + bounds.height) { y = (bounds.y + bounds.height) - popup.getHeight(); } visible = !visible; if (visible) { popup.setLocation(x, y); } popup.setVisible(visible); } @Override public void mouseExited(MouseEvent e) { visible = false; popup.setVisible(visible); } }); popup.addMouseListener( new MouseAdapter() { @Override public void mouseExited(MouseEvent e) { if ((e.getX() < popup.getBounds().getMaxX()) && (e.getX() >= popup.getBounds().getMinX()) && (e.getY() < popup.getBounds().getMaxY()) && (e.getY() >= popup.getBounds().getMinY())) { return; } visible = false; popup.setVisible(visible); } }); } protected JPopupMenu buildSystemTrayJPopupMenu(Stage primaryStage) throws ClassNotFoundException, UnsupportedLookAndFeelException, InstantiationException, IllegalAccessException { final JPopupMenu menu = new JPopupMenu(); final JMenuItem showMenuItem = new JMenuItem("Show"); final JMenuItem exitMenuItem = new JMenuItem("Exit"); menu.add(showMenuItem); menu.addSeparator(); menu.add(exitMenuItem); showMenuItem.addActionListener(ae -> Platform.runLater(primaryStage::show)); exitMenuItem.addActionListener(ae -> System.exit(0)); return menu; } public GraphicsDevice getGraphicsDeviceAt(Point pos) { GraphicsDevice device = null; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice lstGDs[] = ge.getScreenDevices(); ArrayList<GraphicsDevice> lstDevices = new ArrayList<>(lstGDs.length); for (GraphicsDevice gd : lstGDs) { GraphicsConfiguration gc = gd.getDefaultConfiguration(); Rectangle screenBounds = gc.getBounds(); if (screenBounds.contains(pos)) { lstDevices.add(gd); } } if (lstDevices.size() == 1) { device = lstDevices.get(0); } return device; } public Rectangle getScreenViewableBounds(GraphicsDevice gd) { Rectangle bounds = new Rectangle(0, 0, 0, 0); if (gd != null) { GraphicsConfiguration gc = gd.getDefaultConfiguration(); bounds = gc.getBounds(); Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); bounds.x += insets.left; bounds.y += insets.top; bounds.width -= (insets.left + insets.right); bounds.height -= (insets.top + insets.bottom); } return bounds; } }