187 lines
6.5 KiB
Java
187 lines
6.5 KiB
Java
package wtf.beatrice.autosqueal.ui;
|
|
|
|
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
import wtf.beatrice.autosqueal.Main;
|
|
import wtf.beatrice.autosqueal.controls.CursorMover;
|
|
import wtf.beatrice.autosqueal.controls.RobotMouseTracker;
|
|
import wtf.beatrice.autosqueal.listener.CursorMoveListener;
|
|
import wtf.beatrice.autosqueal.util.RunnerUtil;
|
|
import wtf.beatrice.autosqueal.util.SystemUtil;
|
|
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
import java.awt.event.WindowAdapter;
|
|
import java.awt.event.WindowEvent;
|
|
import java.awt.image.BufferedImage;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
public class MainWindow
|
|
{
|
|
private static final Logger LOGGER = LogManager.getLogger(MainWindow.class);
|
|
|
|
private static final int WINDOW_HEIGHT = 700;
|
|
private static final int WINDOW_WIDTH = 800;
|
|
|
|
private static final long AUTOMATION_START_DELAY_SECONDS = 1L;
|
|
private static final long AWAY_POLL_INTERVAL_SECONDS = 1L;
|
|
|
|
private final JFrame frame = new JFrame();
|
|
private Button toggleButton;
|
|
|
|
private ScheduledExecutorService scheduler;
|
|
private CursorMover cursorMover;
|
|
|
|
/**
|
|
* Builds and shows the main window, and starts the automation.
|
|
* Must be called on the EDT.
|
|
*/
|
|
public void init() {
|
|
|
|
frame.setSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
|
|
frame.setTitle("autosqueal");
|
|
frame.setResizable(false);
|
|
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
|
frame.addWindowListener(new WindowAdapter() {
|
|
@Override
|
|
public void windowClosing(WindowEvent e) {
|
|
LOGGER.info("Shutting down...");
|
|
stopAutomation();
|
|
Main.unregisterJNativeHook();
|
|
}
|
|
});
|
|
|
|
toggleButton = new Button();
|
|
toggleButton.setBounds(new Rectangle((WINDOW_WIDTH / 2) - 60, WINDOW_HEIGHT - 60, 120, 30));
|
|
toggleButton.addActionListener(e -> toggleRunning());
|
|
frame.add(toggleButton);
|
|
|
|
int bordersPx = 10;
|
|
int rescaleRateo = ((WINDOW_WIDTH - (2 * bordersPx)) * 100) / RunnerUtil.SCREEN_WIDTH;
|
|
int rescaleWidth = RunnerUtil.SCREEN_WIDTH * rescaleRateo / 100;
|
|
int rescaleHeight = RunnerUtil.SCREEN_HEIGHT * rescaleRateo / 100;
|
|
JLabel imageLabel = new JLabel(new ImageIcon(getScreenCapture(rescaleWidth, rescaleHeight)));
|
|
imageLabel.setBounds(new Rectangle(bordersPx, bordersPx, rescaleWidth, rescaleHeight));
|
|
frame.add(imageLabel);
|
|
|
|
Image preciseScreenshot = getPreciseScreenshot();
|
|
JLabel timestampLabel = new JLabel(new ImageIcon(preciseScreenshot));
|
|
timestampLabel.setBounds(new Rectangle(bordersPx, bordersPx + rescaleHeight + bordersPx, 100, 30));
|
|
frame.add(timestampLabel);
|
|
|
|
frame.setLayout(null);
|
|
frame.setVisible(true);
|
|
|
|
startAutomation();
|
|
updateToggleLabel();
|
|
}
|
|
|
|
private Image getScreenCapture(int rescaleWidth, int rescaleHeight) {
|
|
|
|
Image fullImage = getScreenCapture();
|
|
return fullImage.getScaledInstance(rescaleWidth, rescaleHeight, Image.SCALE_FAST);
|
|
}
|
|
|
|
public Image getPreciseScreenshot() {
|
|
|
|
if(SystemUtil.getHostSystem().equals(SystemUtil.OperatingSystem.MAC_OS)) {
|
|
BufferedImage screenshot = getScreenCapture();
|
|
return screenshot.getSubimage(RunnerUtil.SCREEN_WIDTH - 100,0, 100, 30);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public BufferedImage getScreenCapture() {
|
|
try {
|
|
Robot robot = new Robot();
|
|
return robot.createScreenCapture(new Rectangle(RunnerUtil.SCREEN_WIDTH, RunnerUtil.SCREEN_HEIGHT));
|
|
} catch (AWTException e) {
|
|
LOGGER.error("Robot initialization error", e);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public BufferedImage getBufferedImage(Image img) {
|
|
if (img instanceof BufferedImage image)
|
|
{
|
|
return image;
|
|
}
|
|
|
|
// Create a buffered image with transparency
|
|
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
|
|
|
|
// Draw the image on to the buffered image
|
|
Graphics2D bGr = bimage.createGraphics();
|
|
bGr.drawImage(img, 0, 0, null);
|
|
bGr.dispose();
|
|
|
|
// Return the buffered image
|
|
return bimage;
|
|
}
|
|
|
|
/**
|
|
* Toggles the automation on or off. Safe to call from any thread: the actual
|
|
* work is always marshalled to the EDT, since it touches Swing components.
|
|
*/
|
|
public void toggleRunning() {
|
|
SwingUtilities.invokeLater(() -> {
|
|
if (cursorMover == null) {
|
|
startAutomation();
|
|
} else {
|
|
stopAutomation();
|
|
}
|
|
updateToggleLabel();
|
|
});
|
|
}
|
|
|
|
private void startAutomation() {
|
|
LOGGER.info("Starting automation");
|
|
|
|
scheduler = Executors.newScheduledThreadPool(3, runnable -> {
|
|
Thread thread = new Thread(runnable, "autosqueal-scheduler");
|
|
thread.setDaemon(true);
|
|
return thread;
|
|
});
|
|
|
|
// away-detection: polls the cursor position and ignores the movements
|
|
// the app performs itself, as reported by the robot mouse tracker.
|
|
// the automation only moves the mouse when the user is away.
|
|
RobotMouseTracker robotTracker = new RobotMouseTracker();
|
|
CursorMoveListener cursorMoveListener = new CursorMoveListener(robotTracker);
|
|
scheduler.scheduleWithFixedDelay(cursorMoveListener, 0L, AWAY_POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
|
|
|
cursorMover = new CursorMover(scheduler, robotTracker, cursorMoveListener::isUserAway);
|
|
scheduler.scheduleWithFixedDelay(cursorMover,
|
|
AUTOMATION_START_DELAY_SECONDS,
|
|
RunnerUtil.SECONDS_BETWEEN_MOVES,
|
|
TimeUnit.SECONDS);
|
|
}
|
|
|
|
private void stopAutomation() {
|
|
LOGGER.info("Stopping automation");
|
|
|
|
if (scheduler != null) {
|
|
scheduler.shutdownNow();
|
|
scheduler = null;
|
|
}
|
|
cursorMover = null;
|
|
}
|
|
|
|
private void updateToggleLabel() {
|
|
if (toggleButton == null) {
|
|
// the window is not initialized yet
|
|
return;
|
|
}
|
|
|
|
String hotkey = "[" + NativeKeyEvent.getKeyText(NativeKeyEvent.VC_CONTROL) + "]"
|
|
+ "[" + NativeKeyEvent.getKeyText(NativeKeyEvent.VC_ALT) + "]";
|
|
|
|
toggleButton.setLabel((cursorMover == null ? "Start " : "Stop ") + hotkey);
|
|
}
|
|
|
|
} |