fixed terminate
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
0998ba3762
commit
b3cf1825d9
@ -7,40 +7,43 @@ import java.util.Set;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
/**
|
||||
* Globální hotkey listener pro detekci CTRL+ALT+Z kombinace
|
||||
* Funguje i když není okno fokusované
|
||||
* Globální hotkey listener pro detekci CTRL+ALT+Q.
|
||||
* Na Linuxu funguje i bez fokusu přes nativní polling X11.
|
||||
*/
|
||||
public class GlobalHotkey extends JLabel {
|
||||
private static GlobalHotkey instance;
|
||||
private GlobalKeyListener globalKeyListener;
|
||||
private static final long HOTKEY_COOLDOWN_MS = 700;
|
||||
|
||||
private final GlobalKeyListener globalKeyListener;
|
||||
private final Runnable onHotkey;
|
||||
private volatile boolean running = true;
|
||||
private volatile long lastHotkeyTrigger = 0L;
|
||||
private Thread pollingThread;
|
||||
|
||||
public GlobalHotkey(Runnable onHotkey) {
|
||||
this.globalKeyListener = new GlobalKeyListener(onHotkey);
|
||||
this.onHotkey = onHotkey;
|
||||
this.globalKeyListener = new GlobalKeyListener();
|
||||
|
||||
// Registrace globálního key listeneru pro případy, kdy má aplikace fokus
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.addKeyEventDispatcher(globalKeyListener);
|
||||
|
||||
// Spustit nativní polling pro globální STOP hotkey na Linuxu (funguje i bez fokusu)
|
||||
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
|
||||
Thread pollingThread = new Thread(() -> {
|
||||
while (true) {
|
||||
// Nativní polling globální hotkey na Linuxu (funguje i bez fokusu)
|
||||
if (isLinux()) {
|
||||
pollingThread = new Thread(() -> {
|
||||
while (running && !Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
Thread.sleep(150);
|
||||
// Kontrola CTRL+ALT+X globálně
|
||||
// XK_Control_L/R: 0xFFE3/4, XK_Alt_L/R: 0xFFE9/A, XK_x: 0x78, XK_X: 0x58
|
||||
// XK_Control_L/R: 0xFFE3/4, XK_Alt_L/R: 0xFFE9/A, XK_q: 0x71, XK_Q: 0x51
|
||||
boolean ctrl = WindowFinder.isKeyPressedGlobally(0xFFE3) || WindowFinder.isKeyPressedGlobally(0xFFE4);
|
||||
boolean alt = WindowFinder.isKeyPressedGlobally(0xFFE9) || WindowFinder.isKeyPressedGlobally(0xFFEA);
|
||||
boolean xPressed = WindowFinder.isKeyPressedGlobally(0x78) || WindowFinder.isKeyPressedGlobally(0x58);
|
||||
boolean qPressed = WindowFinder.isKeyPressedGlobally(0x71) || WindowFinder.isKeyPressedGlobally(0x51);
|
||||
|
||||
if (ctrl && alt && xPressed) {
|
||||
if (onHotkey != null) {
|
||||
System.out.println("🔌 Globální NATIVNÍ hotkey detekován: CTRL+ALT+X");
|
||||
onHotkey.run();
|
||||
Thread.sleep(1000); // Prevence vícenásobného spuštění
|
||||
}
|
||||
if (ctrl && alt && qPressed) {
|
||||
triggerHotkey("Globální NATIVNÍ hotkey detekován: CTRL+ALT+Q");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
// Ignorovat chyby v pollingu
|
||||
@ -62,7 +65,7 @@ public class GlobalHotkey extends JLabel {
|
||||
public boolean isKeyPressed(int keyCode) {
|
||||
// Na Linuxu pro šipky používáme výhradně nativní globální detekci.
|
||||
// Standardní KeyEventDispatcher se může "zaseknout", pokud okno ztratí fokus během stisku.
|
||||
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
|
||||
if (isLinux()) {
|
||||
if (keyCode == KeyEvent.VK_DOWN) {
|
||||
try {
|
||||
return WindowFinder.isKeyPressedGlobally(0xFF54); // XK_Down
|
||||
@ -75,17 +78,40 @@ public class GlobalHotkey extends JLabel {
|
||||
}
|
||||
return globalKeyListener.isKeyPressed(keyCode);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
running = false;
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.removeKeyEventDispatcher(globalKeyListener);
|
||||
|
||||
if (pollingThread != null) {
|
||||
pollingThread.interrupt();
|
||||
pollingThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLinux() {
|
||||
return System.getProperty("os.name").toLowerCase().contains("linux");
|
||||
}
|
||||
|
||||
private void triggerHotkey(String logMessage) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastHotkeyTrigger < HOTKEY_COOLDOWN_MS) {
|
||||
return;
|
||||
}
|
||||
lastHotkeyTrigger = now;
|
||||
|
||||
if (onHotkey != null) {
|
||||
System.out.println("🔌 " + logMessage);
|
||||
onHotkey.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interní třída pro globální naslouchání na klávesnici
|
||||
*/
|
||||
private static class GlobalKeyListener implements java.awt.KeyEventDispatcher {
|
||||
private Runnable callback;
|
||||
private class GlobalKeyListener implements java.awt.KeyEventDispatcher {
|
||||
private Set<Integer> pressedKeys = new HashSet<>();
|
||||
|
||||
public GlobalKeyListener(Runnable callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public boolean isKeyPressed(int keyCode) {
|
||||
return pressedKeys.contains(keyCode);
|
||||
@ -96,13 +122,10 @@ public class GlobalHotkey extends JLabel {
|
||||
if (e.getID() == KeyEvent.KEY_PRESSED) {
|
||||
pressedKeys.add(e.getKeyCode());
|
||||
|
||||
// Detekce CTRL+ALT+X
|
||||
if (e.getKeyCode() == KeyEvent.VK_X &&
|
||||
// Detekce CTRL+ALT+Q
|
||||
if (e.getKeyCode() == KeyEvent.VK_Q &&
|
||||
e.isControlDown() && e.isAltDown()) {
|
||||
System.out.println("🔌 Globální hotkey detekován: CTRL+ALT+X");
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
triggerHotkey("Globální hotkey detekován: CTRL+ALT+Q");
|
||||
return true; // Konzumovat event
|
||||
}
|
||||
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
package com.pokemongo;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Insets;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.Point;
|
||||
import java.awt.PointerInfo;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Robot;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
@ -35,6 +31,7 @@ public class PokemonGoAutomation {
|
||||
private static final int DELAY_AFTER_ACTION = 100; // ms
|
||||
private int transferredPokemonCount = 0; // Počet transfernutých pokémonů
|
||||
private Point initialMousePosition = null; // Pozice kurzoru před začátkem automatizace
|
||||
private volatile boolean stopRequested = false;
|
||||
|
||||
public PokemonGoAutomation() throws AWTException {
|
||||
this.robot = new Robot();
|
||||
@ -57,6 +54,14 @@ public class PokemonGoAutomation {
|
||||
transferredPokemonCount = 0;
|
||||
}
|
||||
|
||||
public void requestStop() {
|
||||
stopRequested = true;
|
||||
}
|
||||
|
||||
private boolean shouldStop() {
|
||||
return stopRequested || Thread.currentThread().isInterrupted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pořídí screenshot oblasti okna
|
||||
*/
|
||||
@ -120,6 +125,10 @@ public class PokemonGoAutomation {
|
||||
if (template == null)
|
||||
return 0.0;
|
||||
|
||||
if (shouldStop()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int tWidth = template.getWidth();
|
||||
int tHeight = template.getHeight();
|
||||
|
||||
@ -136,6 +145,9 @@ public class PokemonGoAutomation {
|
||||
int requiredMatch = (int) (totalPixels * 0.75); // 75% shody
|
||||
|
||||
for (int y = 0; y < tHeight; y++) {
|
||||
if (shouldStop()) {
|
||||
return 0.0;
|
||||
}
|
||||
for (int x = 0; x < tWidth; x++) {
|
||||
int templateRGB = template.getRGB(x, y);
|
||||
int screenRGB = screenshot.getRGB(startX + x, startY + y);
|
||||
@ -175,6 +187,10 @@ public class PokemonGoAutomation {
|
||||
private Point findConfirmTransferButtonPosition(BufferedImage template, int tolerance) {
|
||||
System.out.println("Hledám potvrzovací TRANSFER tlačítko pomocí template matchingu...");
|
||||
|
||||
if (shouldStop()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage screenshot = captureScreen(windowBounds);
|
||||
|
||||
int tWidth = template.getWidth();
|
||||
@ -190,6 +206,9 @@ public class PokemonGoAutomation {
|
||||
|
||||
// Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení
|
||||
for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) {
|
||||
if (shouldStop()) {
|
||||
return null;
|
||||
}
|
||||
// Skenovat také X pozice pro nalezení středu shody
|
||||
for (int x = 0; x <= windowBounds.width - tWidth; x += 30) {
|
||||
double score = templateMatch(screenshot, x, y, template, tolerance);
|
||||
@ -218,6 +237,10 @@ public class PokemonGoAutomation {
|
||||
private Point findIncludeButtonPosition(int tolerance) {
|
||||
System.out.println("Hledám tlačítko INCLUDE pomocí template matchingu (include.png)...");
|
||||
|
||||
if (shouldStop()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage screenshot = captureScreen(windowBounds);
|
||||
|
||||
int tWidth = includeTemplate.getWidth();
|
||||
@ -233,6 +256,9 @@ public class PokemonGoAutomation {
|
||||
|
||||
// Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení
|
||||
for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) {
|
||||
if (shouldStop()) {
|
||||
return null;
|
||||
}
|
||||
// Skenovat také X pozice pro nalezení středu shody
|
||||
for (int x = 0; x <= windowBounds.width - tWidth; x += 30) {
|
||||
double score = templateMatch(screenshot, x, y, includeTemplate, tolerance);
|
||||
@ -309,6 +335,10 @@ public class PokemonGoAutomation {
|
||||
* Stiskne tlačítko Transfer na spodní části obrazovky
|
||||
*/
|
||||
public void clickTransferButton() {
|
||||
if (shouldStop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Použít detekci tlačítka
|
||||
// Point buttonPos = findTransferButtonPosition();
|
||||
Point buttonPos = getAbsolutePoint(300, 1156);
|
||||
@ -334,6 +364,10 @@ public class PokemonGoAutomation {
|
||||
public void clickConfirmTransferButton(BufferedImage template, int tolerance) {
|
||||
System.out.println("Potvrzuji transfer - hledám confirmation button...");
|
||||
|
||||
if (shouldStop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
robot.delay(100); // Počkat na zobrazení dialogu
|
||||
|
||||
// Najít zelené TRANSFER tlačítko v potvrzovacím dialogu
|
||||
@ -356,6 +390,10 @@ public class PokemonGoAutomation {
|
||||
public boolean clickIncludeButton(int tolerance) {
|
||||
System.out.println("Potvrzuji transfer - hledám include.png (confirmation button)...");
|
||||
|
||||
if (shouldStop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
robot.delay(100); // Počkat na zobrazení dialogu
|
||||
|
||||
// Najít zelené INCLUDE tlačítko v potvrzovacím dialogu
|
||||
@ -408,11 +446,11 @@ public class PokemonGoAutomation {
|
||||
// Transferovat dokud nedosáhneme požadovaného počtu
|
||||
try {
|
||||
while (transferredCount < totalPokemonCount) {
|
||||
// Kontrola přerušení
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
System.out.println("\n⚠️ Automatizace byla přerušena!");
|
||||
return;
|
||||
}
|
||||
// Kontrola přerušení
|
||||
if (shouldStop()) {
|
||||
System.out.println("\n⚠️ Automatizace byla přerušena!");
|
||||
return;
|
||||
}
|
||||
|
||||
int pokemonThisRound = Math.min(12, totalPokemonCount - transferredCount);
|
||||
|
||||
@ -421,43 +459,53 @@ public class PokemonGoAutomation {
|
||||
+ " pokémonů ===");
|
||||
|
||||
// Vybrat pokémony a získat skutečný počet vybraných
|
||||
int actualTransferredCount = selectAllPokemonCount(pokemonThisRound);
|
||||
int actualTransferredCount = selectAllPokemonCount(pokemonThisRound);
|
||||
|
||||
if (shouldStop()) {
|
||||
System.out.println("\n⚠️ Automatizace byla přerušena během označování!");
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Čekám na TRANSFER...");
|
||||
clickTransferButton();
|
||||
clickTransferButton();
|
||||
|
||||
if (shouldStop()) {
|
||||
System.out.println("\n⚠️ Automatizace byla přerušena po kliknutí na TRANSFER!");
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Čekám na INCLUDE dialog...");
|
||||
robot.delay(100);
|
||||
if (clickIncludeButton(110)) {
|
||||
System.out.println("Čekám na potvrzovací dialog t3...");
|
||||
robot.delay(100);
|
||||
clickConfirmTransferButton(t3Template, 70);
|
||||
} else {
|
||||
System.out.println("Čekám na potvrzovací dialog t2...");
|
||||
robot.delay(100);
|
||||
clickConfirmTransferButton(t2Template, 70);
|
||||
}
|
||||
if (clickIncludeButton(110)) {
|
||||
System.out.println("Čekám na potvrzovací dialog t3...");
|
||||
robot.delay(100);
|
||||
clickConfirmTransferButton(t3Template, 70);
|
||||
} else {
|
||||
System.out.println("Čekám na potvrzovací dialog t2...");
|
||||
robot.delay(100);
|
||||
clickConfirmTransferButton(t2Template, 70);
|
||||
}
|
||||
|
||||
transferredCount += actualTransferredCount;
|
||||
transferredPokemonCount += actualTransferredCount;
|
||||
transferredCount += actualTransferredCount;
|
||||
transferredPokemonCount += actualTransferredCount;
|
||||
|
||||
System.out.println("Transferováno v této iteraci: " + actualTransferredCount + " pokémonů (celkem: "
|
||||
System.out.println("Transferováno v této iteraci: " + actualTransferredCount + " pokémonů (celkem: "
|
||||
+ transferredCount + ")");
|
||||
|
||||
// Pokud ještě zbývá co transferovat, počkat
|
||||
if (transferredCount < totalPokemonCount) {
|
||||
System.out.println("Přestávka: " + delaySeconds + " sekund...");
|
||||
// Čekat s kontrolou přerušení
|
||||
long endTime = System.currentTimeMillis() + (delaySeconds * 1000L);
|
||||
while (System.currentTimeMillis() < endTime) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
System.out.println("\n⚠️ Automatizace byla přerušena během přestávky!");
|
||||
break;
|
||||
if (transferredCount < totalPokemonCount) {
|
||||
System.out.println("Přestávka: " + delaySeconds + " sekund...");
|
||||
// Čekat s kontrolou přerušení
|
||||
long endTime = System.currentTimeMillis() + (delaySeconds * 1000L);
|
||||
while (System.currentTimeMillis() < endTime) {
|
||||
if (shouldStop()) {
|
||||
System.out.println("\n⚠️ Automatizace byla přerušena během přestávky!");
|
||||
break;
|
||||
}
|
||||
robot.delay(100);
|
||||
}
|
||||
robot.delay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Obnovit pozici kurzoru
|
||||
if (initialMousePosition != null) {
|
||||
@ -487,6 +535,11 @@ public class PokemonGoAutomation {
|
||||
System.out.println("Budu označovat " + maxPokemon + " pokémonů...");
|
||||
|
||||
for (int i = 0; i < maxPokemon; i++) {
|
||||
if (shouldStop()) {
|
||||
System.out.println("Označování přerušeno.");
|
||||
return i;
|
||||
}
|
||||
|
||||
Point pos = positions.get(i);
|
||||
System.out.println("Klikám na Pokémona " + (i + 1) + "/" + maxPokemon + " na pozici: " + pos);
|
||||
|
||||
|
||||
@ -96,7 +96,7 @@ public class PokemonGoGUI extends JFrame {
|
||||
versionLabel.setFont(SMALL_FONT);
|
||||
versionLabel.setForeground(new Color(120, 120, 120));
|
||||
|
||||
stopButton = new JButton("⏹ ZASTAVIT (CTRL+ALT+X)");
|
||||
stopButton = new JButton("⏹ ZASTAVIT (CTRL+ALT+Q)");
|
||||
stopButton.setPreferredSize(new Dimension(150, 30));
|
||||
styleButton(stopButton, PRIMARY_RED, Color.WHITE, SMALL_FONT);
|
||||
stopButton.setEnabled(false);
|
||||
@ -261,8 +261,8 @@ public class PokemonGoGUI extends JFrame {
|
||||
|
||||
add(mainPanel);
|
||||
|
||||
// Klávesová zkratka: Ctrl+Alt+Z pro zastavení
|
||||
KeyStroke hotkey = KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK);
|
||||
// Klávesová zkratka: Ctrl+Alt+Q pro zastavení
|
||||
KeyStroke hotkey = KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK);
|
||||
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(hotkey, "stopAutomationAction");
|
||||
getRootPane().getActionMap().put("stopAutomationAction", new AbstractAction() {
|
||||
@Override
|
||||
@ -273,7 +273,7 @@ public class PokemonGoGUI extends JFrame {
|
||||
}
|
||||
});
|
||||
|
||||
// Globální hotkey pro ESC (bonus, když okno není fokusované)
|
||||
// Globální hotkey (funguje i když okno není fokusované)
|
||||
globalHotkey = new GlobalHotkey(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -290,7 +290,7 @@ public class PokemonGoGUI extends JFrame {
|
||||
logArea.append("=== Pokémon GO Automatizace ===\n\n");
|
||||
logArea.append("Vyberte jednu z automatizací a klikněte na tlačítko START.\n");
|
||||
logArea.append("Ujistěte se, že je aplikace Pokémon GO spuštěná.\n");
|
||||
logArea.append("Pro zastavení stiskněte Ctrl+Alt+X nebo klikněte ZASTAVIT.\n\n");
|
||||
logArea.append("Pro zastavení stiskněte Ctrl+Alt+Q nebo klikněte ZASTAVIT.\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -482,6 +482,7 @@ public class PokemonGoGUI extends JFrame {
|
||||
}
|
||||
|
||||
// Získat počet pokémonů a čekání z GUI
|
||||
@SuppressWarnings("unchecked")
|
||||
JComboBox<Integer> countComboBox = (JComboBox<Integer>) transferCard.getClientProperty("countComboBox");
|
||||
JSpinner delaySpinner = (JSpinner) transferCard.getClientProperty("delaySpinner");
|
||||
|
||||
@ -742,10 +743,17 @@ public class PokemonGoGUI extends JFrame {
|
||||
* Zastaví automatizaci
|
||||
*/
|
||||
private void stopAutomation() {
|
||||
if (!isRunning && !autoClickRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
shouldStop = true;
|
||||
autoClickRunning = false;
|
||||
isRunning = false;
|
||||
stopButton.setEnabled(false);
|
||||
|
||||
if (automation != null) {
|
||||
automation.requestStop();
|
||||
}
|
||||
|
||||
if (automationThread != null && automationThread.isAlive()) {
|
||||
automationThread.interrupt();
|
||||
@ -760,6 +768,7 @@ public class PokemonGoGUI extends JFrame {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopButton.setEnabled(false);
|
||||
logArea.append("\n⚠️ Automatizace byla přerušena uživatelem\n");
|
||||
statusLabel.setText("Přerušeno");
|
||||
statusLabel.setForeground(new Color(244, 67, 54));
|
||||
@ -852,6 +861,9 @@ public class PokemonGoGUI extends JFrame {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
System.out.println("Zavírám okno...");
|
||||
if (frame.globalHotkey != null) {
|
||||
frame.globalHotkey.cleanup();
|
||||
}
|
||||
frame.saveSettings();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user