initial commit
50
.gitignore
vendored
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
### Eclipse
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
|
||||||
|
HELP.md
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**
|
||||||
|
!**/src/test/**
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.mvn
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
mvnw
|
||||||
|
mvnw.cmd
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Log4j2 ###
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Ignore Mac DS_Store files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
|
||||||
|
cert.pem
|
||||||
12
pgo-automat-settings.properties
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#Pokémon GO Automatizace - Nastavení
|
||||||
|
#Thu Dec 04 16:03:55 CET 2025
|
||||||
|
autoklik.count=500
|
||||||
|
window.width=807
|
||||||
|
transfer.delay=0
|
||||||
|
window.height=743
|
||||||
|
autoklik.x=2380
|
||||||
|
autoklik.y=1124
|
||||||
|
transfer.count=30
|
||||||
|
window.x=930
|
||||||
|
autoklik.interval=100
|
||||||
|
window.y=22
|
||||||
74
pom.xml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.pokemongo</groupId>
|
||||||
|
<artifactId>pgo-automat</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Java Robot pro automatizaci myši a klávesnice -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.java.dev.jna</groupId>
|
||||||
|
<artifactId>jna</artifactId>
|
||||||
|
<version>5.13.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.java.dev.jna</groupId>
|
||||||
|
<artifactId>jna-platform</artifactId>
|
||||||
|
<version>5.13.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- OpenCV pro rozpoznávání obrazu -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openpnp</groupId>
|
||||||
|
<artifactId>opencv</artifactId>
|
||||||
|
<version>4.7.0-0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>11</source>
|
||||||
|
<target>11</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Maven Shade Plugin - alternativa pro vytvoření Fat JAR -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.4.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>com.pokemongo.PokemonGoGUI</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
<finalName>pgo-automat-gui</finalName>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
64
src/main/java/com/pokemongo/GlobalHotkey.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package com.pokemongo;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.KeyboardFocusManager;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Globální hotkey listener pro detekci CTRL+ALT+Z kombinace
|
||||||
|
* Funguje i když není okno fokusované
|
||||||
|
*/
|
||||||
|
public class GlobalHotkey extends JLabel {
|
||||||
|
private static GlobalHotkey instance;
|
||||||
|
private GlobalKeyListener globalKeyListener;
|
||||||
|
|
||||||
|
public GlobalHotkey(Runnable onHotkey) {
|
||||||
|
this.globalKeyListener = new GlobalKeyListener(onHotkey);
|
||||||
|
|
||||||
|
// Registrace globálního key listeneru
|
||||||
|
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||||
|
.addKeyEventDispatcher(globalKeyListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GlobalHotkey create(Runnable onHotkey) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new GlobalHotkey(onHotkey);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interní třída pro globální naslouchání na klávesnici
|
||||||
|
*/
|
||||||
|
private static class GlobalKeyListener implements java.awt.KeyEventDispatcher {
|
||||||
|
private Runnable callback;
|
||||||
|
private Set<Integer> pressedKeys = new HashSet<>();
|
||||||
|
|
||||||
|
public GlobalKeyListener(Runnable callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchKeyEvent(KeyEvent e) {
|
||||||
|
if (e.getID() == KeyEvent.KEY_PRESSED) {
|
||||||
|
pressedKeys.add(e.getKeyCode());
|
||||||
|
|
||||||
|
// Detekce CTRL+ALT+X
|
||||||
|
if (e.getKeyCode() == KeyEvent.VK_X &&
|
||||||
|
e.isControlDown() && e.isAltDown()) {
|
||||||
|
System.out.println("🔌 Globální hotkey detekován: CTRL+ALT+X");
|
||||||
|
if (callback != null) {
|
||||||
|
callback.run();
|
||||||
|
}
|
||||||
|
return true; // Konzumovat event
|
||||||
|
}
|
||||||
|
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
|
||||||
|
pressedKeys.remove(e.getKeyCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // Nepropagovat ostatní eventy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
651
src/main/java/com/pokemongo/PokemonGoAutomation.java
Normal file
@ -0,0 +1,651 @@
|
|||||||
|
package com.pokemongo;
|
||||||
|
|
||||||
|
import java.awt.AWTException;
|
||||||
|
import java.awt.GraphicsDevice;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Robot;
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatizační nástroj pro Pokémon GO.
|
||||||
|
* Najde běžící aplikaci, označí všechny Pokémony a stiskne tlačítko Transfer.
|
||||||
|
*/
|
||||||
|
public class PokemonGoAutomation {
|
||||||
|
|
||||||
|
private Robot robot;
|
||||||
|
private Rectangle windowBounds;
|
||||||
|
private BufferedImage t1Template; // TRANSFER button template
|
||||||
|
private BufferedImage t2Template; // Confirmation button template
|
||||||
|
private BufferedImage pok1Template; // Pokemon detection template
|
||||||
|
private BufferedImage includeTemplate; // Include button template
|
||||||
|
private static final int DELAY_BETWEEN_CLICKS = 200; // ms
|
||||||
|
private static final int DELAY_AFTER_ACTION = 500; // ms
|
||||||
|
private int transferredPokemonCount = 0; // Počet transfernutých pokémonů
|
||||||
|
|
||||||
|
public PokemonGoAutomation() throws AWTException {
|
||||||
|
this.robot = new Robot();
|
||||||
|
this.robot.setAutoDelay(50);
|
||||||
|
this.robot.setAutoWaitForIdle(true);
|
||||||
|
loadButtonTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vrátí počet dosud transfernutých pokémonů
|
||||||
|
*/
|
||||||
|
public int getTransferredCount() {
|
||||||
|
return transferredPokemonCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resetuje počítadlo transfernutých pokémonů
|
||||||
|
*/
|
||||||
|
public void resetTransferredCount() {
|
||||||
|
transferredPokemonCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pořídí screenshot oblasti okna
|
||||||
|
*/
|
||||||
|
private BufferedImage captureScreen(Rectangle area) {
|
||||||
|
return robot.createScreenCapture(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Načte templaty pro tlačítka TRANSFER (t1.png) a confirmation (t2.png)
|
||||||
|
*/
|
||||||
|
private void loadButtonTemplates() {
|
||||||
|
try {
|
||||||
|
String t1Paths = "/t1.png";
|
||||||
|
|
||||||
|
t1Template = ImageIO.read(getClass().getResourceAsStream(t1Paths));
|
||||||
|
System.out.println("✅ Template TRANSFER načten z: " + t1Paths);
|
||||||
|
|
||||||
|
String t2Paths = "/t2.png";
|
||||||
|
|
||||||
|
t2Template = ImageIO.read(getClass().getResourceAsStream(t2Paths));
|
||||||
|
System.out.println("✅ Template Potvrzení načten z: " + t2Paths);
|
||||||
|
|
||||||
|
String pok1Paths = "/pok1.png";
|
||||||
|
|
||||||
|
pok1Template = ImageIO.read(getClass().getResourceAsStream(pok1Paths));
|
||||||
|
System.out.println("✅ Template Pokémon načten z: " + pok1Paths);
|
||||||
|
|
||||||
|
String includePaths = "/include.png";
|
||||||
|
|
||||||
|
includeTemplate = ImageIO.read(getClass().getResourceAsStream(includePaths));
|
||||||
|
System.out.println("✅ Template INCLUDE načten z: " + includePaths);
|
||||||
|
|
||||||
|
if (t1Template == null) {
|
||||||
|
System.out.println("⚠️ Template t1.png nebyl nalezen");
|
||||||
|
}
|
||||||
|
if (t2Template == null) {
|
||||||
|
System.out.println("⚠️ Template t2.png nebyl nalezen");
|
||||||
|
}
|
||||||
|
if (pok1Template == null) {
|
||||||
|
System.out.println("⚠️ Template pok1.png nebyl nalezen");
|
||||||
|
}
|
||||||
|
if (includeTemplate == null) {
|
||||||
|
System.out.println("⚠️ Template include.png nebyl nalezen");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("⚠️ Chyba při načítání templates: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Porovnává template s oblastí v obrázku - jednoduché pixelové porovnání s
|
||||||
|
* optimalizací
|
||||||
|
*/
|
||||||
|
private double templateMatch(BufferedImage screenshot, int startX, int startY, BufferedImage template, int tolerance) {
|
||||||
|
if (template == null)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
int tWidth = template.getWidth();
|
||||||
|
int tHeight = template.getHeight();
|
||||||
|
|
||||||
|
// Kontrola hranic
|
||||||
|
if (startX + tWidth > screenshot.getWidth() || startY + tHeight > screenshot.getHeight()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
if (startX < 0 || startY < 0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int matchingPixels = 0;
|
||||||
|
int totalPixels = tWidth * tHeight;
|
||||||
|
int requiredMatch = (int) (totalPixels * 0.75); // 75% shody
|
||||||
|
|
||||||
|
for (int y = 0; y < tHeight; y++) {
|
||||||
|
for (int x = 0; x < tWidth; x++) {
|
||||||
|
int templateRGB = template.getRGB(x, y);
|
||||||
|
int screenRGB = screenshot.getRGB(startX + x, startY + y);
|
||||||
|
|
||||||
|
// Rozdělit RGB hodnoty
|
||||||
|
int tR = (templateRGB >> 16) & 0xFF;
|
||||||
|
int tG = (templateRGB >> 8) & 0xFF;
|
||||||
|
int tB = templateRGB & 0xFF;
|
||||||
|
|
||||||
|
int sR = (screenRGB >> 16) & 0xFF;
|
||||||
|
int sG = (screenRGB >> 8) & 0xFF;
|
||||||
|
int sB = screenRGB & 0xFF;
|
||||||
|
|
||||||
|
// Porovnat barvy - zvýšená tolerance pro lepší detekci
|
||||||
|
int rDiff = Math.abs(tR - sR);
|
||||||
|
int gDiff = Math.abs(tG - sG);
|
||||||
|
int bDiff = Math.abs(tB - sB);
|
||||||
|
|
||||||
|
if (rDiff < tolerance && gDiff < tolerance && bDiff < tolerance) {
|
||||||
|
matchingPixels++;
|
||||||
|
|
||||||
|
// Early exit - pokud jsme nedosáhli minima, skončit
|
||||||
|
if (matchingPixels < requiredMatch / 2 && (y * tWidth + x) > (totalPixels / 2)) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vrátit procentuální shodu (0.0-1.0)
|
||||||
|
return (double) matchingPixels / totalPixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detekuje pozice Pokémonů pomocí template matchingu s pok1.png
|
||||||
|
* Hledá v oblasti od vrchu obrazovky do 75% výšky
|
||||||
|
* Může najít až 9 Pokémonů
|
||||||
|
*/
|
||||||
|
private List<Point> detectPokemonsByTemplateMatching() {
|
||||||
|
List<Point> positions = new ArrayList<>();
|
||||||
|
|
||||||
|
System.out.println("\n=== Detekce Pokémonů pomocí template matchingu (pok1.png) ===");
|
||||||
|
|
||||||
|
if (pok1Template == null) {
|
||||||
|
System.out.println("⚠️ Template pok1.png není dostupný");
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage screenshot = captureScreen(windowBounds);
|
||||||
|
|
||||||
|
int tWidth = pok1Template.getWidth();
|
||||||
|
int tHeight = pok1Template.getHeight();
|
||||||
|
System.out.println("Template velikost: " + tWidth + "x" + tHeight);
|
||||||
|
|
||||||
|
// Grid parametry - 3x3 (9 Pokémonů) - pevné pozice podle layoutu aplikace
|
||||||
|
int gridStartY = (int) (windowBounds.height * 0.15);
|
||||||
|
int gridEndY = (int) (windowBounds.height * 0.75);
|
||||||
|
int gridHeight = gridEndY - gridStartY;
|
||||||
|
|
||||||
|
int gridMargin = (int) (windowBounds.width * 0.05);
|
||||||
|
int gridStartX = gridMargin;
|
||||||
|
int gridWidth = windowBounds.width - (2 * gridMargin);
|
||||||
|
|
||||||
|
int columns = 3;
|
||||||
|
int rows = 3; // 3x3 grid = 9 Pokémonů
|
||||||
|
|
||||||
|
int columnWidth = gridWidth / columns;
|
||||||
|
int rowHeight = gridHeight / rows;
|
||||||
|
|
||||||
|
System.out.println("Hledám Pokémony v gridu 3x3 (9 pozic)");
|
||||||
|
|
||||||
|
// Pro každou buňku v gridu hledat template
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
for (int col = 0; col < columns; col++) {
|
||||||
|
int cellX = gridStartX + (col * columnWidth);
|
||||||
|
int cellY = gridStartY + (row * rowHeight);
|
||||||
|
|
||||||
|
double bestScore = 0.0;
|
||||||
|
int bestX = cellX + columnWidth / 2;
|
||||||
|
int bestY = cellY + rowHeight / 2;
|
||||||
|
|
||||||
|
// Skenovat buňku s jemností
|
||||||
|
for (int sy = cellY; sy < cellY + rowHeight - tHeight; sy += 3) {
|
||||||
|
for (int sx = cellX; sx < cellX + columnWidth - tWidth; sx += 3) {
|
||||||
|
double score = templateMatch(screenshot, sx, sy, pok1Template, 115); // Zvýšená tolerance
|
||||||
|
if (score > bestScore) {
|
||||||
|
bestScore = score;
|
||||||
|
bestX = sx + tWidth / 2;
|
||||||
|
bestY = sy + tHeight / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pokud jsme našli match (skóre > 75%), považovat za Pokémona
|
||||||
|
if (bestScore > 0.98) {
|
||||||
|
int x = windowBounds.x + bestX;
|
||||||
|
int y = windowBounds.y + bestY;
|
||||||
|
positions.add(new Point(x, y));
|
||||||
|
System.out.println(" Detekován Pokémon na [" + row + "," + col + "] -> (" + x + ", " + y
|
||||||
|
+ ") skóre=" + String.format("%.1f", bestScore * 100) + "%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Celkem detekováno: " + positions.size() + " Pokémonů");
|
||||||
|
System.out.println("=================================\n");
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Najde a vrátí pozici transferu tlačítka bez kliknutí
|
||||||
|
*/
|
||||||
|
private Point findTransferButtonPosition() {
|
||||||
|
System.out.println("Hledám tlačítko TRANSFER pomocí template matchingu (t1.png)...");
|
||||||
|
|
||||||
|
BufferedImage screenshot = captureScreen(windowBounds);
|
||||||
|
|
||||||
|
int tWidth = t1Template.getWidth();
|
||||||
|
int tHeight = t1Template.getHeight();
|
||||||
|
|
||||||
|
// Template je celý řádek - zkusíme matchovat od 75% do 95% (kde by měl být
|
||||||
|
// TRANSFER řádek)
|
||||||
|
int searchStartY = (int) (windowBounds.height * 0.75);
|
||||||
|
int searchEndY = (int) (windowBounds.height * 0.95);
|
||||||
|
|
||||||
|
double bestScore = 0.0;
|
||||||
|
int bestX = 0;
|
||||||
|
int bestY = 0;
|
||||||
|
|
||||||
|
// Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení
|
||||||
|
for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) {
|
||||||
|
// 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, t1Template, 90);
|
||||||
|
|
||||||
|
if (score > bestScore) {
|
||||||
|
bestScore = score;
|
||||||
|
bestX = x;
|
||||||
|
bestY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestScore > 0.92) {
|
||||||
|
Point bestMatch = new Point(
|
||||||
|
windowBounds.x + bestX + (tWidth / 2),
|
||||||
|
windowBounds.y + bestY + (tHeight / 2));
|
||||||
|
System.out.println("✅ Nalezeno TRANSFER tlačítko (template match) na: " + bestMatch + " (shoda: "
|
||||||
|
+ String.format("%.1f", bestScore * 100) + "%)");
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Template nenalezen");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Najde a vrátí pozici potvrzovacího tlačítka bez kliknutí
|
||||||
|
*/
|
||||||
|
private Point findConfirmTransferButtonPosition() {
|
||||||
|
System.out.println("Hledám potvrzovací TRANSFER tlačítko pomocí template matchingu (t2.png)...");
|
||||||
|
|
||||||
|
BufferedImage screenshot = captureScreen(windowBounds);
|
||||||
|
|
||||||
|
int tWidth = t2Template.getWidth();
|
||||||
|
int tHeight = t2Template.getHeight();
|
||||||
|
|
||||||
|
// Dialog je obvykle v horní polovině po TRANSFER kliknutí
|
||||||
|
int searchStartY = (int) (windowBounds.height * 0.40);
|
||||||
|
int searchEndY = (int) (windowBounds.height * 0.75);
|
||||||
|
|
||||||
|
double bestScore = 0.0;
|
||||||
|
int bestX = 0;
|
||||||
|
int bestY = 0;
|
||||||
|
|
||||||
|
// Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení
|
||||||
|
for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) {
|
||||||
|
// 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, t2Template, 90);
|
||||||
|
|
||||||
|
if (score > bestScore) {
|
||||||
|
bestScore = score;
|
||||||
|
bestX = x;
|
||||||
|
bestY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestScore > 0.85) {
|
||||||
|
Point bestMatch = new Point(
|
||||||
|
windowBounds.x + bestX + (tWidth / 2),
|
||||||
|
windowBounds.y + bestY + (tHeight / 2));
|
||||||
|
System.out.println("✅ Nalezeno potvrzovací tlačítko (template match) na: " + bestMatch + " (shoda: "
|
||||||
|
+ String.format("%.1f", bestScore * 100) + "%)");
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Template nenalezen");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Point findIncludeButtonPosition() {
|
||||||
|
System.out.println("Hledám tlačítko INCLUDE pomocí template matchingu (include.png)...");
|
||||||
|
|
||||||
|
BufferedImage screenshot = captureScreen(windowBounds);
|
||||||
|
|
||||||
|
int tWidth = includeTemplate.getWidth();
|
||||||
|
int tHeight = includeTemplate.getHeight();
|
||||||
|
|
||||||
|
// Dialog je obvykle v horní polovině po TRANSFER kliknutí
|
||||||
|
int searchStartY = (int) (windowBounds.height * 0.40);
|
||||||
|
int searchEndY = (int) (windowBounds.height * 0.75);
|
||||||
|
|
||||||
|
double bestScore = 0.0;
|
||||||
|
int bestX = 0;
|
||||||
|
int bestY = 0;
|
||||||
|
|
||||||
|
// Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení
|
||||||
|
for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) {
|
||||||
|
// 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, 90);
|
||||||
|
|
||||||
|
if (score > bestScore) {
|
||||||
|
bestScore = score;
|
||||||
|
bestX = x;
|
||||||
|
bestY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestScore > 0.92) {
|
||||||
|
Point bestMatch = new Point(
|
||||||
|
windowBounds.x + bestX + (tWidth / 2),
|
||||||
|
windowBounds.y + bestY + (tHeight / 2));
|
||||||
|
System.out.println("✅ Nalezeno tlačítko INCLUDE (template match) na: " + bestMatch + " (shoda: "
|
||||||
|
+ String.format("%.1f", bestScore * 100) + "%)");
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Template nenalezen");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Najde okno s názvem obsahujícím "Pokémon" nebo "Pokemon"
|
||||||
|
*/
|
||||||
|
public boolean findPokemonGoWindow() {
|
||||||
|
try {
|
||||||
|
// Pro Linux - hledání okna pomocí xdotool
|
||||||
|
// Nejdříve najít ID okna, pak získat jeho geometrii
|
||||||
|
Process searchProcess = Runtime.getRuntime().exec(
|
||||||
|
new String[] { "bash", "-c", "xdotool search --name \"Lenovo|L78032\" | head -1" });
|
||||||
|
|
||||||
|
java.io.BufferedReader searchReader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(searchProcess.getInputStream()));
|
||||||
|
|
||||||
|
String windowId = searchReader.readLine();
|
||||||
|
searchProcess.waitFor();
|
||||||
|
|
||||||
|
if (windowId != null && !windowId.trim().isEmpty()) {
|
||||||
|
System.out.println("Nalezeno okno s ID: " + windowId);
|
||||||
|
|
||||||
|
// Získat geometrii okna
|
||||||
|
Process geomProcess = Runtime.getRuntime().exec(
|
||||||
|
new String[] { "bash", "-c", "xdotool getwindowgeometry " + windowId.trim() });
|
||||||
|
|
||||||
|
java.io.BufferedReader geomReader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(geomProcess.getInputStream()));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
int x = 0, y = 0, width = 0, height = 0;
|
||||||
|
|
||||||
|
while ((line = geomReader.readLine()) != null) {
|
||||||
|
System.out.println("xdotool výstup: " + line);
|
||||||
|
if (line.contains("Position:")) {
|
||||||
|
String[] parts = line.split("Position: ")[1].split(",");
|
||||||
|
x = Integer.parseInt(parts[0].trim());
|
||||||
|
y = Integer.parseInt(parts[1].trim().split(" ")[0]);
|
||||||
|
} else if (line.contains("Geometry:")) {
|
||||||
|
String[] parts = line.split("Geometry: ")[1].split("x");
|
||||||
|
width = Integer.parseInt(parts[0].trim());
|
||||||
|
height = Integer.parseInt(parts[1].trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
geomProcess.waitFor();
|
||||||
|
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
windowBounds = new Rectangle(x, y, width, height);
|
||||||
|
System.out.println("Nalezeno okno: " + windowBounds);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback - použití celé obrazovky
|
||||||
|
System.out.println("Okno nenalezeno, použiji celou obrazovku");
|
||||||
|
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
||||||
|
windowBounds = gd.getDefaultConfiguration().getBounds();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Chyba při hledání okna: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
// Použití celé obrazovky jako záložní varianta
|
||||||
|
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
||||||
|
windowBounds = gd.getDefaultConfiguration().getBounds();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktivuje okno aplikace
|
||||||
|
*/
|
||||||
|
public void activateWindow() {
|
||||||
|
try {
|
||||||
|
// Kliknutí na horní panel okna (title bar) pro aktivaci
|
||||||
|
// Title bar je obvykle 30-40px vysoký v horní části okna
|
||||||
|
int centerX = windowBounds.x + windowBounds.width / 2;
|
||||||
|
int titleBarY = windowBounds.y + 15; // 15px od vrcholu okna = horní panel
|
||||||
|
|
||||||
|
System.out.println("Aktivuji okno kliknutím na title bar: (" + centerX + ", " + titleBarY + ")");
|
||||||
|
|
||||||
|
robot.mouseMove(centerX, titleBarY);
|
||||||
|
robot.delay(200);
|
||||||
|
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.delay(DELAY_AFTER_ACTION);
|
||||||
|
|
||||||
|
System.out.println("Okno aktivováno");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Chyba při aktivaci okna: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stiskne tlačítko Transfer na spodní části obrazovky
|
||||||
|
*/
|
||||||
|
public void clickTransferButton() {
|
||||||
|
// Použít detekci tlačítka
|
||||||
|
Point buttonPos = findTransferButtonPosition();
|
||||||
|
|
||||||
|
if (buttonPos == null) {
|
||||||
|
System.err.println("Tlačítko TRANSFER nebylo nalezeno!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
robot.mouseMove(buttonPos.x, buttonPos.y);
|
||||||
|
robot.delay(DELAY_AFTER_ACTION);
|
||||||
|
|
||||||
|
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
|
||||||
|
System.out.println("Tlačítko TRANSFER stisknuto!");
|
||||||
|
robot.delay(DELAY_AFTER_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Potvrdí transfer (pokud je potřeba potvrzovací dialog)
|
||||||
|
*/
|
||||||
|
public void clickConfirmTransferButton() {
|
||||||
|
System.out.println("Potvrzuji transfer - hledám t2.png (confirmation button)...");
|
||||||
|
|
||||||
|
robot.delay(100); // Počkat na zobrazení dialogu
|
||||||
|
|
||||||
|
// Najít zelené TRANSFER tlačítko v potvrzovacím dialogu
|
||||||
|
Point buttonPos = findConfirmTransferButtonPosition();
|
||||||
|
if (buttonPos == null) {
|
||||||
|
System.out.println("Potvrzovací tlačítko nebylo nalezeno, přeskočím potvrzení.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
robot.mouseMove(buttonPos.x, buttonPos.y);
|
||||||
|
robot.delay(DELAY_BETWEEN_CLICKS);
|
||||||
|
|
||||||
|
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
|
||||||
|
System.out.println("Transfer finálně potvrzen!");
|
||||||
|
robot.delay(DELAY_AFTER_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clickIncludeButton() {
|
||||||
|
System.out.println("Potvrzuji transfer - hledám include.png (confirmation button)...");
|
||||||
|
|
||||||
|
robot.delay(100); // Počkat na zobrazení dialogu
|
||||||
|
|
||||||
|
// Najít zelené INCLUDE tlačítko v potvrzovacím dialogu
|
||||||
|
Point buttonPos = findIncludeButtonPosition();
|
||||||
|
if (buttonPos == null) {
|
||||||
|
System.out.println("INCLUDE tlačítko nebylo nalezeno, přeskočím potvrzení.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
robot.mouseMove(buttonPos.x, buttonPos.y);
|
||||||
|
robot.delay(DELAY_BETWEEN_CLICKS);
|
||||||
|
|
||||||
|
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
|
||||||
|
System.out.println("Transfer finálně potvrzen!");
|
||||||
|
robot.delay(DELAY_AFTER_ACTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spustí Transfer automatizaci s konkrétním počtem pokémonů
|
||||||
|
*
|
||||||
|
* @param totalPokemonCount Celkový počet pokémonů k transferu
|
||||||
|
* @param delaySeconds Čekání mezi iteracemi (v sekundách)
|
||||||
|
*/
|
||||||
|
public void runWithCount(int totalPokemonCount, int delaySeconds) {
|
||||||
|
System.out.println("Spouštím Transfer automatizaci - Počet pokémonů: " + totalPokemonCount + ", Čekání: "
|
||||||
|
+ delaySeconds + "s");
|
||||||
|
|
||||||
|
if (!findPokemonGoWindow()) {
|
||||||
|
System.err.println("Nepodařilo se najít okno Pokémon GO!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activateWindow();
|
||||||
|
|
||||||
|
System.out.println("Čekám 1 sekundu před začátkem...");
|
||||||
|
robot.delay(1000);
|
||||||
|
|
||||||
|
int transferredCount = 0;
|
||||||
|
|
||||||
|
// Transferovat dokud nedosáhneme požadovaného počtu
|
||||||
|
while (transferredCount < totalPokemonCount) {
|
||||||
|
// Kontrola přerušení
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
|
System.out.println("\n⚠️ Automatizace byla přerušena!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pokemonThisRound = Math.min(9, totalPokemonCount - transferredCount);
|
||||||
|
|
||||||
|
System.out.println("\n=== Iterace " + (transferredCount / 9 + 1) + " - Transferuji maximálně " + pokemonThisRound
|
||||||
|
+ " pokémonů ===");
|
||||||
|
|
||||||
|
// Vybrat pokémony a získat skutečný počet vybraných
|
||||||
|
int actualTransferredCount = selectAllPokemonCount(pokemonThisRound);
|
||||||
|
|
||||||
|
System.out.println("Čekám na TRANSFER...");
|
||||||
|
clickTransferButton();
|
||||||
|
|
||||||
|
System.out.println("Čekám na INCLUDE dialog...");
|
||||||
|
robot.delay(100);
|
||||||
|
clickIncludeButton();
|
||||||
|
|
||||||
|
System.out.println("Čekám na potvrzovací dialog...");
|
||||||
|
robot.delay(100);
|
||||||
|
clickConfirmTransferButton();
|
||||||
|
|
||||||
|
transferredCount += actualTransferredCount;
|
||||||
|
transferredPokemonCount += actualTransferredCount;
|
||||||
|
|
||||||
|
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!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
robot.delay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("\n✅ Transfer automatizace dokončena! Transferováno: " + transferredCount + " pokémonů");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Označí konkrétní počet pokémonů
|
||||||
|
* @return Skutečný počet vybraných pokémonů
|
||||||
|
*/
|
||||||
|
private int selectAllPokemonCount(int count) {
|
||||||
|
System.out.println("Začínám označovat " + count + " pokémonů (pouze template matching)...");
|
||||||
|
|
||||||
|
List<Point> positions = detectPokemonsByTemplateMatching();
|
||||||
|
|
||||||
|
int maxPokemon = Math.min(count, positions.size());
|
||||||
|
System.out.println("Budu označovat " + maxPokemon + " pokémonů...");
|
||||||
|
|
||||||
|
for (int i = 0; i < maxPokemon; i++) {
|
||||||
|
Point pos = positions.get(i);
|
||||||
|
System.out.println("Klikám na Pokémona " + (i + 1) + "/" + maxPokemon + " na pozici: " + pos);
|
||||||
|
|
||||||
|
robot.mouseMove(pos.x, pos.y);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
robot.delay(500);
|
||||||
|
System.out.println(" -> Dlouhé podržení (aktivace multi-select)");
|
||||||
|
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.delay(800);
|
||||||
|
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.delay(DELAY_AFTER_ACTION);
|
||||||
|
} else {
|
||||||
|
System.out.println(" -> Normální klik");
|
||||||
|
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
robot.delay(DELAY_BETWEEN_CLICKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Označeno " + maxPokemon + " pokémonů!");
|
||||||
|
System.out.println("Čekám před kliknutím na TRANSFER...");
|
||||||
|
robot.delay(800);
|
||||||
|
|
||||||
|
return maxPokemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== Pokémon GO Automatizační Nástroj ===");
|
||||||
|
System.out.println("Ujistěte se, že je aplikace Pokémon GO spuštěná...");
|
||||||
|
}
|
||||||
|
}
|
||||||
830
src/main/java/com/pokemongo/PokemonGoGUI.java
Normal file
@ -0,0 +1,830 @@
|
|||||||
|
package com.pokemongo;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jednoduchá GUI aplikace pro Pokémon GO automatizaci
|
||||||
|
* S kartami pro jednotlivé automatizace
|
||||||
|
*/
|
||||||
|
public class PokemonGoGUI extends JFrame {
|
||||||
|
|
||||||
|
private JButton stopButton;
|
||||||
|
private JTextArea logArea;
|
||||||
|
private JLabel statusLabel;
|
||||||
|
private volatile boolean isRunning = false;
|
||||||
|
private volatile boolean shouldStop = false;
|
||||||
|
private PokemonGoAutomation automation;
|
||||||
|
private Thread automationThread;
|
||||||
|
private int totalTransferredCount = 0; // Celkový počet transfernutých pokémonů
|
||||||
|
|
||||||
|
private volatile boolean autoClickRunning = false;
|
||||||
|
private GlobalHotkey globalHotkey;
|
||||||
|
|
||||||
|
private static final String CONFIG_FILE = "pgo-automat-settings.properties";
|
||||||
|
private static final String VERSION = "1.0.2";
|
||||||
|
private Properties settings;
|
||||||
|
private JSpinner countSpinner;
|
||||||
|
private JSpinner delaySpinner;
|
||||||
|
|
||||||
|
private static final Font TITLE_FONT = new Font("Segoe UI", Font.BOLD, 18);
|
||||||
|
private static final Font HEADER_FONT = new Font("Segoe UI", Font.BOLD, 12);
|
||||||
|
private static final Font BODY_FONT = new Font("Segoe UI", Font.PLAIN, 11);
|
||||||
|
private static final Font SMALL_FONT = new Font("Segoe UI", Font.PLAIN, 10);
|
||||||
|
private static final Color PANEL_BACKGROUND = new Color(248, 248, 252);
|
||||||
|
private static final Color CARD_BACKGROUND = Color.WHITE;
|
||||||
|
private static final Color CARD_BORDER_COLOR = new Color(210, 210, 210);
|
||||||
|
private static final Color PRIMARY_GREEN = new Color(76, 175, 80);
|
||||||
|
private static final Color PRIMARY_RED = new Color(244, 67, 54);
|
||||||
|
private static final Color ACCENT_BLUE = new Color(33, 150, 243);
|
||||||
|
private static final Color LOG_BORDER_COLOR = new Color(200, 200, 200);
|
||||||
|
private static final Color STATUS_DEFAULT = new Color(33, 150, 243);
|
||||||
|
private static final int CARD_MAX_WIDTH = 760;
|
||||||
|
|
||||||
|
public PokemonGoGUI() {
|
||||||
|
setTitle("Pokémon GO Automatizace v" + VERSION);
|
||||||
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
|
// Načtení uložených nastavení
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
// Obnovení velikosti a pozice okna
|
||||||
|
int width = Integer.parseInt(settings.getProperty("window.width", "900"));
|
||||||
|
int height = Integer.parseInt(settings.getProperty("window.height", "680"));
|
||||||
|
int x = Integer.parseInt(settings.getProperty("window.x", "-1"));
|
||||||
|
int y = Integer.parseInt(settings.getProperty("window.y", "-1"));
|
||||||
|
|
||||||
|
setSize(width, height);
|
||||||
|
|
||||||
|
if (x >= 0 && y >= 0) {
|
||||||
|
setLocation(x, y);
|
||||||
|
} else {
|
||||||
|
setLocationRelativeTo(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
setResizable(true);
|
||||||
|
setBackground(PANEL_BACKGROUND);
|
||||||
|
|
||||||
|
// Uložení nastavení při zavření okna
|
||||||
|
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent e) {
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hlavní panel
|
||||||
|
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||||
|
mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
|
||||||
|
mainPanel.setBackground(PANEL_BACKGROUND);
|
||||||
|
|
||||||
|
// Horní panel se stavem
|
||||||
|
JLabel stateLabel = new JLabel("Stav:");
|
||||||
|
stateLabel.setFont(HEADER_FONT);
|
||||||
|
stateLabel.setForeground(new Color(70, 70, 70));
|
||||||
|
statusLabel = new JLabel("Připraven k spuštění");
|
||||||
|
statusLabel.setFont(BODY_FONT);
|
||||||
|
statusLabel.setForeground(STATUS_DEFAULT);
|
||||||
|
JLabel versionLabel = new JLabel("verze " + VERSION);
|
||||||
|
versionLabel.setFont(SMALL_FONT);
|
||||||
|
versionLabel.setForeground(new Color(120, 120, 120));
|
||||||
|
|
||||||
|
stopButton = new JButton("⏹ ZASTAVIT (CTRL+ALT+X)");
|
||||||
|
stopButton.setPreferredSize(new Dimension(150, 30));
|
||||||
|
styleButton(stopButton, PRIMARY_RED, Color.WHITE, SMALL_FONT);
|
||||||
|
stopButton.setEnabled(false);
|
||||||
|
stopButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
stopAutomation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JPanel stopPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||||
|
stopPanel.setOpaque(false);
|
||||||
|
stopPanel.add(stopButton);
|
||||||
|
|
||||||
|
JLabel titleLabel = new JLabel("Pokémon GO Automatizace");
|
||||||
|
titleLabel.setFont(TITLE_FONT);
|
||||||
|
titleLabel.setForeground(new Color(30, 30, 30));
|
||||||
|
|
||||||
|
JPanel titleRow = new JPanel(new BorderLayout());
|
||||||
|
titleRow.setBackground(PANEL_BACKGROUND);
|
||||||
|
titleRow.add(titleLabel, BorderLayout.WEST);
|
||||||
|
titleRow.add(stopPanel, BorderLayout.EAST);
|
||||||
|
|
||||||
|
JPanel statusRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
|
||||||
|
statusRow.setBackground(PANEL_BACKGROUND);
|
||||||
|
statusRow.add(stateLabel);
|
||||||
|
statusRow.add(statusLabel);
|
||||||
|
statusRow.add(versionLabel);
|
||||||
|
|
||||||
|
JPanel headerPanel = new JPanel();
|
||||||
|
headerPanel.setLayout(new BoxLayout(headerPanel, BoxLayout.Y_AXIS));
|
||||||
|
headerPanel.setBackground(PANEL_BACKGROUND);
|
||||||
|
headerPanel.add(titleRow);
|
||||||
|
headerPanel.add(Box.createVerticalStrut(6));
|
||||||
|
headerPanel.add(statusRow);
|
||||||
|
|
||||||
|
// Panel s kartami automatizací
|
||||||
|
JPanel cardsContainer = new JPanel(new GridBagLayout());
|
||||||
|
cardsContainer.setBackground(PANEL_BACKGROUND);
|
||||||
|
cardsContainer.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||||
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
gbc.gridx = 0;
|
||||||
|
gbc.gridy = 0;
|
||||||
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
gbc.weightx = 1.0;
|
||||||
|
gbc.anchor = GridBagConstraints.NORTH;
|
||||||
|
gbc.insets = new Insets(0, 0, 12, 0);
|
||||||
|
|
||||||
|
// Karta 1: Transfer automatizace
|
||||||
|
JPanel transferCard = createAutomationCard(
|
||||||
|
"🔄 TRANSFER AUTOMATIZACE",
|
||||||
|
"Hledá Pokémony, označí je a stiskne Transfer",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Najít START tlačítko - je v EAST pozici BorderLayoutu
|
||||||
|
JButton startBtnTransfer = findStartButton(transferCard);
|
||||||
|
|
||||||
|
if (startBtnTransfer != null) {
|
||||||
|
startBtnTransfer.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
startTransferAutomation(transferCard);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
gbc.gridy = 0;
|
||||||
|
cardsContainer.add(transferCard, gbc);
|
||||||
|
gbc.gridy++;
|
||||||
|
gbc.insets = new Insets(0, 0, 12, 0);
|
||||||
|
|
||||||
|
// Karta 2: Autoklik
|
||||||
|
JPanel autoClickCard = createAutomationCard(
|
||||||
|
"🖱️ AUTOKLIK",
|
||||||
|
"Automaticky klikuje na zadanou pozici",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Najít START tlačítko
|
||||||
|
JButton startBtnAutoClick = findStartButton(autoClickCard);
|
||||||
|
|
||||||
|
if (startBtnAutoClick != null) {
|
||||||
|
startBtnAutoClick.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
startAutoClick(autoClickCard);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
gbc.gridy++;
|
||||||
|
gbc.insets = new Insets(0, 0, 0, 0);
|
||||||
|
cardsContainer.add(autoClickCard, gbc);
|
||||||
|
gbc.gridy++;
|
||||||
|
gbc.weighty = 1.0;
|
||||||
|
JPanel spacer = new JPanel();
|
||||||
|
spacer.setOpaque(false);
|
||||||
|
cardsContainer.add(spacer, gbc);
|
||||||
|
|
||||||
|
JPanel automationPanel = new JPanel(new BorderLayout());
|
||||||
|
automationPanel.setBackground(PANEL_BACKGROUND);
|
||||||
|
automationPanel.add(cardsContainer, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
JScrollPane automationScrollPane = new JScrollPane(automationPanel);
|
||||||
|
automationScrollPane.setBorder(null);
|
||||||
|
automationScrollPane.setBackground(PANEL_BACKGROUND);
|
||||||
|
automationScrollPane.getViewport().setBackground(PANEL_BACKGROUND);
|
||||||
|
|
||||||
|
// Log area
|
||||||
|
logArea = new JTextArea();
|
||||||
|
logArea.setEditable(false);
|
||||||
|
logArea.setFont(new Font("Consolas", Font.PLAIN, 11));
|
||||||
|
logArea.setBackground(new Color(248, 248, 248));
|
||||||
|
logArea.setForeground(new Color(45, 45, 45));
|
||||||
|
logArea.setLineWrap(true);
|
||||||
|
logArea.setWrapStyleWord(true);
|
||||||
|
logArea.setMargin(new Insets(5, 5, 5, 5));
|
||||||
|
|
||||||
|
JScrollPane logScrollPane = new JScrollPane(logArea);
|
||||||
|
logScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
|
||||||
|
logScrollPane.setBorder(BorderFactory.createTitledBorder(
|
||||||
|
BorderFactory.createLineBorder(LOG_BORDER_COLOR),
|
||||||
|
"Log",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
HEADER_FONT,
|
||||||
|
new Color(85, 85, 85)
|
||||||
|
));
|
||||||
|
logScrollPane.getViewport().setBackground(new Color(247, 247, 247));
|
||||||
|
|
||||||
|
JButton clearLogButton = new JButton("🗑 Vyčistit log");
|
||||||
|
clearLogButton.setPreferredSize(new Dimension(140, 28));
|
||||||
|
styleButton(clearLogButton, ACCENT_BLUE, Color.WHITE, SMALL_FONT);
|
||||||
|
clearLogButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
logArea.setText("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JPanel logButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 5));
|
||||||
|
logButtonPanel.setBackground(PANEL_BACKGROUND);
|
||||||
|
logButtonPanel.add(clearLogButton);
|
||||||
|
|
||||||
|
JPanel logPanel = new JPanel(new BorderLayout(5, 5));
|
||||||
|
logPanel.setBackground(new Color(247, 247, 247));
|
||||||
|
logPanel.add(logScrollPane, BorderLayout.CENTER);
|
||||||
|
logPanel.add(logButtonPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
// Splitter mezi kartami a logem
|
||||||
|
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, automationScrollPane, logPanel);
|
||||||
|
splitPane.setDividerSize(6);
|
||||||
|
splitPane.setResizeWeight(0.6);
|
||||||
|
splitPane.setContinuousLayout(true);
|
||||||
|
splitPane.setBorder(null);
|
||||||
|
splitPane.setBackground(PANEL_BACKGROUND);
|
||||||
|
|
||||||
|
// Přidání do hlavního panelu
|
||||||
|
mainPanel.add(headerPanel, BorderLayout.NORTH);
|
||||||
|
mainPanel.add(splitPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
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);
|
||||||
|
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(hotkey, "stopAutomationAction");
|
||||||
|
getRootPane().getActionMap().put("stopAutomationAction", new AbstractAction() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (isRunning) {
|
||||||
|
stopAutomation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Globální hotkey pro ESC (bonus, když okno není fokusované)
|
||||||
|
globalHotkey = new GlobalHotkey(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isRunning) {
|
||||||
|
stopAutomation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mainPanel.add(globalHotkey, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
// Přesměrování System.out a System.err do GUI
|
||||||
|
redirectSystemStreams();
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vytvoří kartu automatizace s nastavením
|
||||||
|
*/
|
||||||
|
private JPanel createAutomationCard(String title, String description, ActionListener startAction) {
|
||||||
|
JPanel card = new JPanel(new BorderLayout(12, 12));
|
||||||
|
card.setBackground(CARD_BACKGROUND);
|
||||||
|
card.setBorder(BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createLineBorder(CARD_BORDER_COLOR, 1),
|
||||||
|
BorderFactory.createEmptyBorder(8, 8, 8, 8)
|
||||||
|
));
|
||||||
|
card.setMaximumSize(new Dimension(CARD_MAX_WIDTH, 160));
|
||||||
|
card.setPreferredSize(new Dimension(CARD_MAX_WIDTH, 160));
|
||||||
|
card.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||||
|
JPanel infoPanel = new JPanel();
|
||||||
|
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS));
|
||||||
|
infoPanel.setBackground(CARD_BACKGROUND);
|
||||||
|
infoPanel.setOpaque(true);
|
||||||
|
infoPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
|
||||||
|
|
||||||
|
JLabel titleLabel = new JLabel(title);
|
||||||
|
titleLabel.setFont(HEADER_FONT);
|
||||||
|
titleLabel.setForeground(new Color(28, 28, 28));
|
||||||
|
|
||||||
|
JLabel descLabel = new JLabel("<html><body style='width:210px'>" + description + "</body></html>");
|
||||||
|
descLabel.setFont(SMALL_FONT);
|
||||||
|
descLabel.setForeground(new Color(115, 115, 115));
|
||||||
|
|
||||||
|
infoPanel.add(titleLabel);
|
||||||
|
infoPanel.add(Box.createVerticalStrut(3));
|
||||||
|
infoPanel.add(descLabel);
|
||||||
|
infoPanel.add(Box.createVerticalGlue());
|
||||||
|
|
||||||
|
JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 12, 6));
|
||||||
|
centerPanel.setBackground(CARD_BACKGROUND);
|
||||||
|
centerPanel.setOpaque(true);
|
||||||
|
|
||||||
|
if (title.contains("TRANSFER")) {
|
||||||
|
countSpinner = new JSpinner(new SpinnerNumberModel(
|
||||||
|
Integer.parseInt(settings.getProperty("transfer.count", "9")),
|
||||||
|
1, 999, 1
|
||||||
|
));
|
||||||
|
countSpinner.setPreferredSize(new Dimension(64, 26));
|
||||||
|
delaySpinner = new JSpinner(new SpinnerNumberModel(
|
||||||
|
Integer.parseInt(settings.getProperty("transfer.delay", "2")),
|
||||||
|
0, 60, 1
|
||||||
|
));
|
||||||
|
delaySpinner.setPreferredSize(new Dimension(64, 26));
|
||||||
|
|
||||||
|
card.putClientProperty("countSpinner", countSpinner);
|
||||||
|
card.putClientProperty("delaySpinner", delaySpinner);
|
||||||
|
|
||||||
|
centerPanel.add(createFieldPanel("Počet:", countSpinner));
|
||||||
|
centerPanel.add(createFieldPanel("Čekání (s):", delaySpinner));
|
||||||
|
} else if (title.contains("AUTOKLIK")) {
|
||||||
|
JSpinner xSpinner = new JSpinner(new SpinnerNumberModel(
|
||||||
|
Integer.parseInt(settings.getProperty("autoklik.x", "960")),
|
||||||
|
0, 9999, 1
|
||||||
|
));
|
||||||
|
xSpinner.setPreferredSize(new Dimension(70, 26));
|
||||||
|
card.putClientProperty("xSpinner", xSpinner);
|
||||||
|
|
||||||
|
JSpinner ySpinner = new JSpinner(new SpinnerNumberModel(
|
||||||
|
Integer.parseInt(settings.getProperty("autoklik.y", "540")),
|
||||||
|
0, 9999, 1
|
||||||
|
));
|
||||||
|
ySpinner.setPreferredSize(new Dimension(70, 26));
|
||||||
|
card.putClientProperty("ySpinner", ySpinner);
|
||||||
|
|
||||||
|
JSpinner intervalSpinner = new JSpinner(new SpinnerNumberModel(
|
||||||
|
Integer.parseInt(settings.getProperty("autoklik.interval", "100")),
|
||||||
|
10, 10000, 10
|
||||||
|
));
|
||||||
|
intervalSpinner.setPreferredSize(new Dimension(70, 26));
|
||||||
|
card.putClientProperty("intervalSpinner", intervalSpinner);
|
||||||
|
|
||||||
|
JSpinner clickCountSpinner = new JSpinner(new SpinnerNumberModel(
|
||||||
|
Integer.parseInt(settings.getProperty("autoklik.count", "10")),
|
||||||
|
1, 10000, 1
|
||||||
|
));
|
||||||
|
clickCountSpinner.setPreferredSize(new Dimension(70, 26));
|
||||||
|
card.putClientProperty("clickCountSpinner", clickCountSpinner);
|
||||||
|
|
||||||
|
centerPanel.add(createFieldPanel("X:", xSpinner));
|
||||||
|
centerPanel.add(createFieldPanel("Y:", ySpinner));
|
||||||
|
centerPanel.add(createFieldPanel("Interval (ms):", intervalSpinner));
|
||||||
|
centerPanel.add(createFieldPanel("Počet kliknutí:", clickCountSpinner));
|
||||||
|
|
||||||
|
JButton pickPositionBtn = new JButton("Nastavit");
|
||||||
|
pickPositionBtn.setPreferredSize(new Dimension(80, 26));
|
||||||
|
styleButton(pickPositionBtn, ACCENT_BLUE, Color.WHITE, SMALL_FONT);
|
||||||
|
pickPositionBtn.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
PositionPicker picker = new PositionPicker(null);
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
picker.setVisible(true);
|
||||||
|
|
||||||
|
while (picker.isVisible()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (picker.isPositionSelected()) {
|
||||||
|
Point pos = picker.getSelectedPosition();
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
xSpinner.setValue(pos.x);
|
||||||
|
ySpinner.setValue(pos.y);
|
||||||
|
logArea.append("✅ (" + pos.x + ", " + pos.y + ")\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
centerPanel.add(createFieldPanel("Pozice:", pickPositionBtn));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pravý panel - START tlačítko
|
||||||
|
JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0));
|
||||||
|
rightPanel.setBackground(CARD_BACKGROUND);
|
||||||
|
rightPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
|
||||||
|
|
||||||
|
JButton startBtn = new JButton("▶ START");
|
||||||
|
startBtn.setPreferredSize(new Dimension(90, 32));
|
||||||
|
styleButton(startBtn, PRIMARY_GREEN, Color.WHITE, SMALL_FONT);
|
||||||
|
startBtn.addActionListener(startAction);
|
||||||
|
|
||||||
|
rightPanel.add(startBtn);
|
||||||
|
|
||||||
|
// Komponování karty: info nahoře, nastavení uprostřed, tlačítko dole
|
||||||
|
card.add(infoPanel, BorderLayout.NORTH);
|
||||||
|
card.add(centerPanel, BorderLayout.CENTER);
|
||||||
|
card.add(rightPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel createFieldPanel(String labelText, JComponent field) {
|
||||||
|
JLabel label = new JLabel(labelText);
|
||||||
|
label.setFont(BODY_FONT);
|
||||||
|
label.setForeground(new Color(96, 96, 96));
|
||||||
|
field.setFont(BODY_FONT);
|
||||||
|
if (field instanceof JSpinner) {
|
||||||
|
((JSpinner) field).setBorder(BorderFactory.createLineBorder(CARD_BORDER_COLOR));
|
||||||
|
field.setBackground(new Color(252, 252, 252));
|
||||||
|
}
|
||||||
|
JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 2));
|
||||||
|
wrapper.setBackground(CARD_BACKGROUND);
|
||||||
|
wrapper.add(label);
|
||||||
|
wrapper.add(field);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void styleButton(JButton button, Color background, Color foreground, Font font) {
|
||||||
|
button.setFont(font);
|
||||||
|
button.setBackground(background);
|
||||||
|
button.setForeground(foreground);
|
||||||
|
button.setFocusPainted(false);
|
||||||
|
button.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JButton findStartButton(Container container) {
|
||||||
|
for (Component comp : container.getComponents()) {
|
||||||
|
if (comp instanceof JButton) {
|
||||||
|
JButton button = (JButton) comp;
|
||||||
|
if ("▶ START".equals(button.getText())) {
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (comp instanceof Container) {
|
||||||
|
JButton nested = findStartButton((Container) comp);
|
||||||
|
if (nested != null) {
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Přesměruje System.out a System.err do log area
|
||||||
|
*/
|
||||||
|
private void redirectSystemStreams() {
|
||||||
|
System.setOut(new java.io.PrintStream(System.out) {
|
||||||
|
@Override
|
||||||
|
public void println(String x) {
|
||||||
|
super.println(x);
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logArea.append(x + "\n");
|
||||||
|
logArea.setCaretPosition(logArea.getDocument().getLength());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
System.setErr(new java.io.PrintStream(System.err) {
|
||||||
|
@Override
|
||||||
|
public void println(String x) {
|
||||||
|
super.println(x);
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logArea.append("[CHYBA] " + x + "\n");
|
||||||
|
logArea.setCaretPosition(logArea.getDocument().getLength());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spustí Transfer automatizaci
|
||||||
|
*/
|
||||||
|
private void startTransferAutomation(JPanel transferCard) {
|
||||||
|
if (isRunning) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Automatizace již běží!", "Upozornění", JOptionPane.WARNING_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Získat počet pokémonů a čekání z GUI
|
||||||
|
JSpinner countSpinner = (JSpinner) transferCard.getClientProperty("countSpinner");
|
||||||
|
JSpinner delaySpinner = (JSpinner) transferCard.getClientProperty("delaySpinner");
|
||||||
|
|
||||||
|
int pokemonCount = (Integer) countSpinner.getValue();
|
||||||
|
int delaySeconds = (Integer) delaySpinner.getValue();
|
||||||
|
|
||||||
|
stopButton.setEnabled(true);
|
||||||
|
statusLabel.setText("Transfer automatizace běží...");
|
||||||
|
statusLabel.setForeground(new Color(255, 152, 0));
|
||||||
|
isRunning = true;
|
||||||
|
|
||||||
|
logArea.append("\n========================================\n");
|
||||||
|
logArea.append("SPOUŠTĚNÍ TRANSFER AUTOMATIZACE\n");
|
||||||
|
logArea.append("Počet pokémonů: " + pokemonCount + ", Čekání: " + delaySeconds + "s\n");
|
||||||
|
logArea.append("========================================\n\n");
|
||||||
|
|
||||||
|
shouldStop = false;
|
||||||
|
automationThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
automation = new PokemonGoAutomation();
|
||||||
|
automation.resetTransferredCount();
|
||||||
|
|
||||||
|
// Spustit vlákno pro aktualizaci statusu
|
||||||
|
Thread statusUpdateThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (isRunning && !shouldStop && !Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(500); // Aktualizovat každých 500ms
|
||||||
|
final int transferred = automation.getTransferredCount();
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isRunning) {
|
||||||
|
statusLabel.setText("Transfer běží... Transferováno: " + transferred + "/" + pokemonCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
statusUpdateThread.setDaemon(true);
|
||||||
|
statusUpdateThread.start();
|
||||||
|
|
||||||
|
if (!shouldStop) {
|
||||||
|
automation.runWithCount(pokemonCount, delaySeconds);
|
||||||
|
totalTransferredCount += automation.getTransferredCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldStop) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
statusLabel.setText("Automatizace dokončena! Celkem transferováno: " + totalTransferredCount);
|
||||||
|
statusLabel.setForeground(new Color(76, 175, 80));
|
||||||
|
logArea.append("\n✅ Transfer automatizace úspěšně dokončena! Transferováno: " + automation.getTransferredCount() + " pokémonů\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
statusLabel.setText("Chyba!");
|
||||||
|
statusLabel.setForeground(new Color(244, 67, 54));
|
||||||
|
logArea.append("\n❌ Chyba: " + e.getMessage() + "\n");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
isRunning = false;
|
||||||
|
stopButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
automationThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spustí autoklik
|
||||||
|
*/
|
||||||
|
private void startAutoClick(JPanel autoClickCard) {
|
||||||
|
if (isRunning) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Automatizace již běží!", "Upozornění", JOptionPane.WARNING_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Načtení nastavení
|
||||||
|
JSpinner xSpinner = (JSpinner) autoClickCard.getClientProperty("xSpinner");
|
||||||
|
JSpinner ySpinner = (JSpinner) autoClickCard.getClientProperty("ySpinner");
|
||||||
|
JSpinner intervalSpinner = (JSpinner) autoClickCard.getClientProperty("intervalSpinner");
|
||||||
|
JSpinner clickCountSpinner = (JSpinner) autoClickCard.getClientProperty("clickCountSpinner");
|
||||||
|
|
||||||
|
int x = (Integer) xSpinner.getValue();
|
||||||
|
int y = (Integer) ySpinner.getValue();
|
||||||
|
int interval = (Integer) intervalSpinner.getValue();
|
||||||
|
int clickCount = (Integer) clickCountSpinner.getValue();
|
||||||
|
|
||||||
|
// Uložení nastavení
|
||||||
|
settings.setProperty("autoklik.x", String.valueOf(x));
|
||||||
|
settings.setProperty("autoklik.y", String.valueOf(y));
|
||||||
|
settings.setProperty("autoklik.interval", String.valueOf(interval));
|
||||||
|
settings.setProperty("autoklik.count", String.valueOf(clickCount));
|
||||||
|
saveSettings();
|
||||||
|
|
||||||
|
stopButton.setEnabled(true);
|
||||||
|
statusLabel.setText("Autoklik běží... (" + x + ", " + y + ")");
|
||||||
|
statusLabel.setForeground(new Color(255, 152, 0));
|
||||||
|
isRunning = true;
|
||||||
|
autoClickRunning = true;
|
||||||
|
shouldStop = false;
|
||||||
|
|
||||||
|
logArea.append("\n========================================\n");
|
||||||
|
logArea.append("SPOUŠTĚNÍ AUTOKLIKERU\n");
|
||||||
|
logArea.append("Pozice: (" + x + ", " + y + ")\n");
|
||||||
|
logArea.append("Interval: " + interval + "ms, Počet kliknutí: " + clickCount + "\n");
|
||||||
|
logArea.append("========================================\n\n");
|
||||||
|
|
||||||
|
automationThread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
java.awt.Robot robot = new java.awt.Robot();
|
||||||
|
|
||||||
|
logArea.append("Spouštím autoklik...\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < clickCount && autoClickRunning && !shouldStop; i++) {
|
||||||
|
// Přesunutí kurzoru na pozici
|
||||||
|
robot.mouseMove(x, y);
|
||||||
|
Thread.sleep(10);
|
||||||
|
|
||||||
|
// Klik
|
||||||
|
robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK);
|
||||||
|
|
||||||
|
// Čekání mezi kliknutími
|
||||||
|
if (i < clickCount - 1 && !shouldStop) {
|
||||||
|
Thread.sleep(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress každých 10 kliknutí
|
||||||
|
if ((i + 1) % 10 == 0 && !shouldStop) {
|
||||||
|
int progress = i + 1;
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logArea.append(" Kliknutí: " + progress + "/" + clickCount + "\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldStop) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
statusLabel.setText("Autoklik úspěšně dokončen!");
|
||||||
|
statusLabel.setForeground(new Color(76, 175, 80));
|
||||||
|
logArea.append("\n✅ Autoklik úspěšně dokončen! Celkem kliknutí: " + clickCount + "\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (!shouldStop) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
statusLabel.setText("Chyba!");
|
||||||
|
statusLabel.setForeground(new Color(244, 67, 54));
|
||||||
|
logArea.append("\n❌ Chyba: " + e.getMessage() + "\n");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
isRunning = false;
|
||||||
|
autoClickRunning = false;
|
||||||
|
stopButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
automationThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zastaví automatizaci
|
||||||
|
*/
|
||||||
|
private void stopAutomation() {
|
||||||
|
shouldStop = true;
|
||||||
|
autoClickRunning = false;
|
||||||
|
isRunning = false;
|
||||||
|
stopButton.setEnabled(false);
|
||||||
|
|
||||||
|
if (automationThread != null && automationThread.isAlive()) {
|
||||||
|
automationThread.interrupt();
|
||||||
|
// Čekej max 2 sekundy na ukončení
|
||||||
|
try {
|
||||||
|
automationThread.join(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logArea.append("\n⚠️ Automatizace byla přerušena uživatelem\n");
|
||||||
|
statusLabel.setText("Přerušeno");
|
||||||
|
statusLabel.setForeground(new Color(244, 67, 54));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Načte uložená nastavení ze souboru
|
||||||
|
*/
|
||||||
|
private void loadSettings() {
|
||||||
|
settings = new Properties();
|
||||||
|
File configFile = new File(CONFIG_FILE);
|
||||||
|
|
||||||
|
if (configFile.exists()) {
|
||||||
|
try (java.io.FileInputStream fis = new java.io.FileInputStream(configFile)) {
|
||||||
|
settings.load(fis);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Chyba při načítání nastavení: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Výchozí hodnoty, pokud nastavení neexistují
|
||||||
|
if (!settings.containsKey("transfer.count")) {
|
||||||
|
settings.setProperty("transfer.count", "9");
|
||||||
|
}
|
||||||
|
if (!settings.containsKey("transfer.delay")) {
|
||||||
|
settings.setProperty("transfer.delay", "2");
|
||||||
|
}
|
||||||
|
if (!settings.containsKey("window.width")) {
|
||||||
|
settings.setProperty("window.width", "700");
|
||||||
|
}
|
||||||
|
if (!settings.containsKey("window.height")) {
|
||||||
|
settings.setProperty("window.height", "600");
|
||||||
|
}
|
||||||
|
if (!settings.containsKey("window.x")) {
|
||||||
|
settings.setProperty("window.x", "-1");
|
||||||
|
}
|
||||||
|
if (!settings.containsKey("window.y")) {
|
||||||
|
settings.setProperty("window.y", "-1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uloží aktuální nastavení do souboru
|
||||||
|
*/
|
||||||
|
private void saveSettings() {
|
||||||
|
// Uložení hodnot z spinnerů
|
||||||
|
if (countSpinner != null) {
|
||||||
|
settings.setProperty("transfer.count", countSpinner.getValue().toString());
|
||||||
|
}
|
||||||
|
if (delaySpinner != null) {
|
||||||
|
settings.setProperty("transfer.delay", delaySpinner.getValue().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uložení velikosti a pozice okna
|
||||||
|
settings.setProperty("window.width", String.valueOf(getWidth()));
|
||||||
|
settings.setProperty("window.height", String.valueOf(getHeight()));
|
||||||
|
settings.setProperty("window.x", String.valueOf(getX()));
|
||||||
|
settings.setProperty("window.y", String.valueOf(getY()));
|
||||||
|
|
||||||
|
try (java.io.FileOutputStream fos = new java.io.FileOutputStream(CONFIG_FILE)) {
|
||||||
|
settings.store(fos, "Pokémon GO Automatizace - Nastavení");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Chyba při ukládání nastavení: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
PokemonGoGUI frame = new PokemonGoGUI();
|
||||||
|
frame.setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/main/java/com/pokemongo/PositionPicker.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package com.pokemongo;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
public class PositionPicker extends JWindow {
|
||||||
|
private Point selectedPosition = null;
|
||||||
|
private boolean positionSelected = false;
|
||||||
|
private PositionPickerListener listener = null;
|
||||||
|
|
||||||
|
public interface PositionPickerListener {
|
||||||
|
void onPositionSelected(Point position);
|
||||||
|
void onCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PositionPicker(PositionPickerListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
||||||
|
Rectangle bounds = gd.getDefaultConfiguration().getBounds();
|
||||||
|
setBounds(bounds);
|
||||||
|
|
||||||
|
System.out.println("PositionPicker vytvořen: " + bounds);
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
|
setBackground(new Color(0, 0, 0, 0));
|
||||||
|
|
||||||
|
// Přidáme panel pro vykreslování
|
||||||
|
JPanel panel = new JPanel() {
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
// Lehké šedé pozadí
|
||||||
|
g2d.setColor(new Color(100, 100, 100, 10));
|
||||||
|
g2d.fillRect(0, 0, getWidth(), getHeight());
|
||||||
|
|
||||||
|
// Instrukce
|
||||||
|
g2d.setColor(new Color(0, 0, 0, 200));
|
||||||
|
g2d.fillRect(10, 10, 400, 60);
|
||||||
|
|
||||||
|
g2d.setColor(new Color(255, 255, 0, 255));
|
||||||
|
g2d.setFont(new Font("Monospaced", Font.BOLD, 12));
|
||||||
|
g2d.drawString("VYBERTE POZICI - Klikněte na místo kde chcete klikat", 20, 30);
|
||||||
|
g2d.drawString("ESC: Zrušit", 20, 50);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
panel.setOpaque(false);
|
||||||
|
panel.setBackground(new Color(0, 0, 0, 0));
|
||||||
|
setContentPane(panel);
|
||||||
|
|
||||||
|
panel.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
selectedPosition = new Point(e.getX(), e.getY());
|
||||||
|
positionSelected = true;
|
||||||
|
System.out.println("✅ Pozice vybrána: " + selectedPosition);
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onPositionSelected(selectedPosition);
|
||||||
|
}
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.addKeyListener(new KeyAdapter() {
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||||
|
System.out.println("ESC - Výběr zrušen");
|
||||||
|
positionSelected = false;
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onCancelled();
|
||||||
|
}
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.setFocusable(true);
|
||||||
|
panel.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point getSelectedPosition() {
|
||||||
|
return selectedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPositionSelected() {
|
||||||
|
return positionSelected;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/main/resources/include.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/main/resources/pok1.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/main/resources/ps-end/ps-end.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
src/main/resources/ps-end/ps-end2.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
src/main/resources/ps-end/ps-end3.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164033.png
Executable file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164048.png
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164057.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164107.png
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164119.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164207.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164638.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/main/resources/ps/Screenshot 2025-10-03 164654.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/main/resources/t1.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/t2.png
Normal file
|
After Width: | Height: | Size: 17 KiB |