├── .gitignore ├── .travis.yml ├── ADBWiFiConnect.iml ├── ADBWiFiConnectGUI ├── build.gradle └── src │ └── main │ ├── java │ └── dk │ │ └── appdictive │ │ └── adbwificonnect │ │ ├── ADBCommands.java │ │ ├── Device.java │ │ ├── IPHelper.java │ │ ├── ListViewCell.java │ │ ├── Main.java │ │ ├── OnScreenConsoleOutputDelegate.java │ │ └── SerializeHelper.java │ └── resources │ ├── icons │ ├── ic_adbremoteconnect.png │ └── ic_adbremoteconnect@2x.png │ ├── list_cell_item.fxml │ ├── main.fxml │ └── stylesheet.css ├── ADBWiFiConnect_screen1.png ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── dk │ └── appdictive │ └── adbwificonnect │ └── plugin │ ├── OSValidator.java │ └── RemoteConnect.java └── resources ├── META-INF └── plugin.xml └── icons ├── ic_adbremoteconnect.png └── ic_adbremoteconnect@2x.png /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ 2 | out/ 3 | .idea/ 4 | target/ 5 | *.iml 6 | local.properties 7 | 8 | ### Java ### 9 | *.class 10 | classes/ 11 | 12 | # Package Files # 13 | *.jar 14 | !ADBWiFiConnectGUI/build/libs/ADBWiFiConnectGUI.jar 15 | 16 | ### Gradle ### 17 | .gradle 18 | build/ 19 | 20 | ### Specific ### 21 | *.zip 22 | 23 | # Ignore Gradle GUI config 24 | gradle-app.setting 25 | 26 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 27 | !gradle-wrapper.jar 28 | 29 | # Avoid ignoring ADBWiFiConnect.iml file to be able to import the project into any IntelliJ IDE and run/debug the pulgin without problems 30 | !ADBWiFiConnect.iml 31 | 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: oraclejdk7 3 | env: 4 | matrix: 5 | - TERM=dumb 6 | after_success: 7 | - ./gradlew test jacocoTestReport coveralls 8 | 9 | script: 10 | ./gradlew checkstyle build 11 | -------------------------------------------------------------------------------- /ADBWiFiConnect.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.github.appdictive' 2 | 3 | apply plugin: 'java' 4 | apply plugin: 'application' 5 | 6 | sourceCompatibility = 1.8 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | mainClassName = "dk.appdictive.adbwificonnect.Main" 13 | 14 | dependencies { 15 | // compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '1.2.16' 16 | // compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '1.2.16' 17 | compile 'log4j:log4j:1.2.17' 18 | 19 | //JFoenix does not work with all linux distributions it seems (issues reported on Github) 20 | // compile 'com.jfoenix:jfoenix:1.4.0' 21 | } 22 | 23 | jar { 24 | from { 25 | configurations.compile.collect { 26 | it.isDirectory() ? it : zipTree(it) 27 | } 28 | configurations.runtime.collect { 29 | it.isDirectory() ? it : zipTree(it) 30 | } 31 | } 32 | manifest { 33 | attributes( 34 | 'Main-Class': 'dk.appdictive.adbwificonnect.Main' 35 | ) 36 | // 'Class-Path': configurations.compile.collect { it.getName() }.join(' '), 37 | } 38 | } -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/java/dk/appdictive/adbwificonnect/ADBCommands.java: -------------------------------------------------------------------------------- 1 | package dk.appdictive.adbwificonnect; 2 | 3 | import javafx.application.Platform; 4 | import javafx.scene.control.Button; 5 | import org.apache.log4j.Logger; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | 11 | /** 12 | * Created by JesperLR on 05-11-2016. 13 | */ 14 | public class ADBCommands { 15 | 16 | private static final String ADB_DEVICE_PORT = "5555"; 17 | private static Logger log = Logger.getLogger(Main.class.getName()); 18 | 19 | public static String runCommand(String command) { 20 | try 21 | { 22 | Process p=Runtime.getRuntime().exec(command); 23 | p.waitFor(); 24 | BufferedReader reader=new BufferedReader( 25 | new InputStreamReader(p.getInputStream()) 26 | ); 27 | String result = ""; 28 | String line; 29 | while((line = reader.readLine()) != null) 30 | { 31 | result += line + "\n"; 32 | } 33 | result = result.replace("\n\n", "\n"); 34 | return result; 35 | } 36 | catch(IOException e1) {log.error(e1.getMessage());} 37 | catch(InterruptedException e2) {log.error(e2.getMessage());} 38 | 39 | return null; 40 | } 41 | 42 | public static String runCommand(String[] cmd) 43 | { 44 | String s; 45 | String results = ""; 46 | try { 47 | Runtime rt = Runtime.getRuntime(); 48 | Process p = rt.exec(cmd, null); 49 | 50 | BufferedReader stdInput = new BufferedReader(new 51 | InputStreamReader(p.getInputStream())); 52 | BufferedReader stdError = new BufferedReader(new 53 | InputStreamReader(p.getErrorStream())); 54 | // read the output from the command 55 | //System.out.println("Here is the standard output of the command:\n"); 56 | while ((s = stdInput.readLine()) != null) 57 | { 58 | System.out.println(s); 59 | results = results + s; 60 | } 61 | // read any errors from the attempted command 62 | //System.out.println("Here is the standard error of the command (if any):\n"); 63 | while ((s = stdError.readLine()) != null) 64 | { 65 | System.out.println(s); 66 | results = results + s; 67 | } 68 | return results.toString(); 69 | } 70 | catch (IOException e) 71 | { 72 | log.error(e.getMessage()); 73 | return e.getMessage(); 74 | } 75 | } 76 | 77 | private static String getAdbSpecificDevice(Device device) { 78 | String adbCommand = Main.adbPath + " -s "; 79 | if (device.getType() == Device.DEVICE_TYPE_USB) { 80 | adbCommand += device.getSerialID(); 81 | } else if (device.getType() == Device.DEVICE_TYPE_REMOTE) { 82 | adbCommand += device.getRemoteIP()+":"+ADB_DEVICE_PORT; 83 | } 84 | return adbCommand; 85 | } 86 | 87 | public static String getDeviceName(Device device) { 88 | String model = runCommand(getAdbSpecificDevice(device) + " shell getprop ro.product.model"); 89 | String manufacturer = runCommand(getAdbSpecificDevice(device) + " shell getprop ro.product.manufacturer"); 90 | String androidVersionName = runCommand(getAdbSpecificDevice(device) + " shell getprop ro.build.version.release"); 91 | String androidVersionCode = runCommand(getAdbSpecificDevice(device) + " shell getprop ro.build.version.sdk"); 92 | 93 | String deviceName = manufacturer.trim() + " " + model.trim() + " (Android " + androidVersionName.trim() + ", SDK " + androidVersionCode.trim() + ")"; 94 | 95 | return deviceName; 96 | } 97 | 98 | public static String getDeviceSerialNo(Device device) { 99 | String output = runCommand(getAdbSpecificDevice(device) + " shell getprop ro.serialno"); 100 | String serialNumber = output.trim(); 101 | return serialNumber; 102 | } 103 | 104 | public static String getDeviceIP(Device device) { 105 | String output = runCommand(getAdbSpecificDevice(device) + " shell ip -f inet addr show wlan0"); 106 | 107 | String ipFromText = IPHelper.getIPFromText(output); 108 | if (ipFromText == null) { 109 | log.error("Could not get device IP - make sure the device is connected to your WiFi"); 110 | } 111 | return ipFromText; 112 | } 113 | 114 | public static boolean disconnectDevice(Device device) { 115 | String output = runCommand(Main.adbPath + " disconnect " + device.getRemoteIP() + ":" + ADB_DEVICE_PORT); 116 | if (output.contains("disconnected")) { 117 | return true; 118 | } else { 119 | return false; 120 | } 121 | } 122 | 123 | public static void disconnectDeviceAsync(Device device) { 124 | Runnable r = new Runnable() { 125 | public void run() { 126 | disconnectDevice(device); 127 | } 128 | }; 129 | new Thread(r).start(); 130 | } 131 | 132 | public static boolean usbConnectToDevice(Device device) { 133 | runCommand(getAdbSpecificDevice(device) + " tcpip " + ADB_DEVICE_PORT); 134 | String output = runCommand(Main.adbPath + " connect " + device.getRemoteIP() + ":" + ADB_DEVICE_PORT); 135 | log.debug(output); 136 | if (output.contains("unable")) { 137 | return false; 138 | } else { 139 | return true; 140 | } 141 | } 142 | 143 | public static void usbConnectToDeviceAsync(Device device, Button connectButton) { 144 | connectButton.setDisable(true); 145 | Runnable r = new Runnable() { 146 | public void run() { 147 | usbConnectToDevice(device); 148 | 149 | Platform.runLater(new Runnable() { 150 | @Override 151 | public void run() { 152 | if (connectButton != null) connectButton.setDisable(false); 153 | } 154 | }); 155 | } 156 | }; 157 | new Thread(r).start(); 158 | } 159 | 160 | public static boolean remoteConnectToDevice(Device device) { 161 | String output = runCommand(Main.adbPath + " connect " + device.getRemoteIP() + ":" + ADB_DEVICE_PORT); 162 | log.debug(output); 163 | if (output.contains("unable")) { 164 | return false; 165 | } else { 166 | return true; 167 | } 168 | } 169 | 170 | public static void remoteConnectToDeviceAsync(Device device, Button connectButton) { 171 | connectButton.setDisable(true); 172 | Runnable r = new Runnable() { 173 | public void run() { 174 | remoteConnectToDevice(device); 175 | 176 | Platform.runLater(new Runnable() { 177 | @Override 178 | public void run() { 179 | if (connectButton != null) connectButton.setDisable(false); 180 | } 181 | }); 182 | } 183 | }; 184 | new Thread(r).start(); 185 | } 186 | 187 | 188 | } 189 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/java/dk/appdictive/adbwificonnect/Device.java: -------------------------------------------------------------------------------- 1 | package dk.appdictive.adbwificonnect; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by JesperLR on 05-11-2016. 7 | */ 8 | public class Device implements Serializable { 9 | 10 | public static int DEVICE_TYPE_USB = 0, DEVICE_TYPE_REMOTE = 1, DEVICE_TYPE_SAVED_REMOTE = 2, DEVICE_TYPE_OFFLINE = 3; 11 | 12 | public Device() { 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public void setName(String name) { 20 | this.name = name; 21 | } 22 | 23 | public String getRemoteIP() { 24 | return remoteIP; 25 | } 26 | 27 | public void setRemoteIP(String remoteIP) { 28 | this.remoteIP = remoteIP; 29 | } 30 | 31 | public String getSerialID() { 32 | return serialID; 33 | } 34 | 35 | public void setSerialID(String serialID) { 36 | this.serialID = serialID; 37 | } 38 | 39 | public int getType() { 40 | return type; 41 | } 42 | 43 | public void setType(int type) { 44 | this.type = type; 45 | } 46 | 47 | String name; 48 | String remoteIP; 49 | String serialID; 50 | int type; 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/java/dk/appdictive/adbwificonnect/IPHelper.java: -------------------------------------------------------------------------------- 1 | package dk.appdictive.adbwificonnect; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * Created by JesperLR on 05-11-2016. 8 | */ 9 | public class IPHelper { 10 | 11 | private static final String IP_ADDRESS_REGEX = "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}"; 12 | 13 | public static String getIPFromText(String text) { 14 | Pattern ipAddressPattern = Pattern.compile(IP_ADDRESS_REGEX); 15 | 16 | Matcher m = ipAddressPattern.matcher(text); 17 | String ipAddress = null; 18 | while (m.find()) { 19 | String s = m.group(0); 20 | if (ipAddress == null) { 21 | //we assume the first IP address is the one we want to address 22 | ipAddress = s; 23 | } 24 | } 25 | return ipAddress; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/java/dk/appdictive/adbwificonnect/ListViewCell.java: -------------------------------------------------------------------------------- 1 | package dk.appdictive.adbwificonnect; 2 | 3 | import javafx.event.ActionEvent; 4 | import javafx.event.EventHandler; 5 | import javafx.fxml.FXML; 6 | import javafx.fxml.FXMLLoader; 7 | import javafx.fxml.Initializable; 8 | import javafx.scene.control.Button; 9 | import javafx.scene.control.Label; 10 | import javafx.scene.control.ListCell; 11 | import javafx.scene.control.TextInputDialog; 12 | import javafx.scene.input.MouseEvent; 13 | import javafx.scene.layout.HBox; 14 | 15 | import java.io.IOException; 16 | import java.net.URL; 17 | import java.util.Optional; 18 | import java.util.ResourceBundle; 19 | 20 | public class ListViewCell extends ListCell implements Initializable 21 | { 22 | 23 | FXMLLoader fxmlLoader; 24 | 25 | @FXML 26 | private HBox hBox; 27 | @FXML 28 | private Label connectionType; 29 | @FXML 30 | private Label deviceName; 31 | @FXML 32 | private Label deviceAddress; 33 | @FXML 34 | private Button connectAndSaveButton; 35 | @FXML 36 | private Button deleteAndDisconnectButton; 37 | 38 | private Main main; 39 | 40 | public ListViewCell(Main main) { 41 | this.main = main; 42 | } 43 | 44 | @Override 45 | public void updateItem(Device device, boolean empty) 46 | { 47 | super.updateItem(device,empty); 48 | 49 | if(empty || device == null) { 50 | setText(null); 51 | setGraphic(null); 52 | } else { 53 | if (fxmlLoader == null) { 54 | fxmlLoader = new FXMLLoader(getClass().getResource("/list_cell_item.fxml")); 55 | fxmlLoader.setController(this); 56 | 57 | try { 58 | fxmlLoader.load(); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | deviceName.setText(device.getName()); 65 | deleteAndDisconnectButton.setVisible(true); 66 | connectAndSaveButton.setDisable(false); 67 | 68 | if (device.getType() == Device.DEVICE_TYPE_USB) { 69 | deleteAndDisconnectButton.setVisible(false); 70 | connectAndSaveButton.setText("CONNECT"); 71 | connectionType.setText("USB"); 72 | deviceAddress.setText(device.getSerialID()); 73 | } else if (device.getType() == Device.DEVICE_TYPE_REMOTE) { 74 | deleteAndDisconnectButton.setText("DISCONNECT"); 75 | connectionType.setText("WI-FI"); 76 | deviceAddress.setText(device.getRemoteIP()); 77 | connectAndSaveButton.setText("SAVE"); 78 | if (main.hasRemoteIPSaved(device.getRemoteIP())) { 79 | connectAndSaveButton.setDisable(true); 80 | } 81 | } else if (device.getType() == Device.DEVICE_TYPE_SAVED_REMOTE) { 82 | if (main.isCurrentlyConnectedToRemoteIP(device.getRemoteIP())) { 83 | connectAndSaveButton.setDisable(true); 84 | } 85 | connectAndSaveButton.setText("CONNECT"); 86 | connectionType.setText(""); 87 | deviceAddress.setText(device.getRemoteIP()); 88 | deleteAndDisconnectButton.setText("DELETE"); 89 | } else { 90 | connectAndSaveButton.setVisible(false); 91 | deleteAndDisconnectButton.setVisible(false); 92 | if (device.getType() == Device.DEVICE_TYPE_OFFLINE) connectionType.setText("OFFLINE"); 93 | } 94 | 95 | setText(null); 96 | setGraphic(hBox); 97 | } 98 | 99 | } 100 | 101 | @Override 102 | public void initialize(URL location, ResourceBundle resources) { 103 | setOnMouseClicked(new EventHandler() { 104 | @Override 105 | public void handle(MouseEvent event) { 106 | Device item = getItem(); 107 | if (item != null && item.getType() == Device.DEVICE_TYPE_SAVED_REMOTE){ 108 | TextInputDialog textInputDialog = new TextInputDialog(item.getName()); 109 | textInputDialog.setTitle("Set device name"); 110 | textInputDialog.setHeaderText(""); 111 | Optional s = textInputDialog.showAndWait(); 112 | 113 | if (s.isPresent() && !s.get().trim().isEmpty()) { 114 | item.setName(s.get().trim()); 115 | main.saveConnection(item); 116 | } 117 | } 118 | } 119 | }); 120 | 121 | connectAndSaveButton.setOnAction(new EventHandler() { 122 | @Override 123 | public void handle(ActionEvent event) { 124 | Device item = getItem(); 125 | if (item.getType() == Device.DEVICE_TYPE_USB) { 126 | ADBCommands.usbConnectToDeviceAsync(item, connectAndSaveButton); 127 | } else if (item.getType() == Device.DEVICE_TYPE_SAVED_REMOTE){ 128 | ADBCommands.remoteConnectToDeviceAsync(item, connectAndSaveButton); 129 | } else if (item.getType() == Device.DEVICE_TYPE_REMOTE){ 130 | main.saveConnection(item); 131 | } 132 | } 133 | }); 134 | 135 | deleteAndDisconnectButton.setOnAction(new EventHandler() { 136 | @Override 137 | public void handle(ActionEvent event) { 138 | Device item = getItem(); 139 | if (item.getType() == Device.DEVICE_TYPE_REMOTE) { 140 | ADBCommands.disconnectDeviceAsync(item); 141 | } else if (item.getType() == Device.DEVICE_TYPE_SAVED_REMOTE){ 142 | main.deleteConnection(item); 143 | } 144 | } 145 | }); 146 | } 147 | } -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/java/dk/appdictive/adbwificonnect/Main.java: -------------------------------------------------------------------------------- 1 | package dk.appdictive.adbwificonnect; 2 | 3 | import javafx.application.Application; 4 | import javafx.application.Platform; 5 | import javafx.beans.value.ChangeListener; 6 | import javafx.beans.value.ObservableValue; 7 | import javafx.collections.FXCollections; 8 | import javafx.collections.ObservableList; 9 | import javafx.event.ActionEvent; 10 | import javafx.event.EventHandler; 11 | import javafx.fxml.FXML; 12 | import javafx.fxml.FXMLLoader; 13 | import javafx.fxml.Initializable; 14 | import javafx.scene.Parent; 15 | import javafx.scene.Scene; 16 | import javafx.scene.control.*; 17 | import javafx.scene.image.Image; 18 | import javafx.stage.Stage; 19 | import javafx.stage.WindowEvent; 20 | import javafx.util.Callback; 21 | import org.apache.log4j.Logger; 22 | 23 | import java.net.URL; 24 | import java.util.*; 25 | import java.util.prefs.BackingStoreException; 26 | import java.util.prefs.Preferences; 27 | 28 | public class Main extends Application implements Initializable { 29 | 30 | private static final String PREF_JAR_LOCATION = "JAR_LOCATION", PREF_ADB_LOCATION = "ADB_LOCATION", PREF_SAVED_CONNECTIONS = "SAVED_CONNECTIONS", PREFS_ID = "dk/appdictive/adbconnect"; 31 | 32 | @FXML 33 | private TextArea outputTextArea; 34 | @FXML 35 | private CheckMenuItem menuItemDebug; 36 | @FXML 37 | private ListView listView; 38 | @FXML 39 | private ListView listViewSaved; 40 | ObservableList observableList = FXCollections.observableArrayList(); 41 | ObservableList observableListSavedConnections = FXCollections.observableArrayList(); 42 | private boolean isWindowActive = true; 43 | private Preferences prefs; 44 | private Logger log = Logger.getLogger(Main.class.getName()); 45 | private OnScreenConsoleOutputDelegate onScreenConsoleOutputDelegate; 46 | public static String adbPath; 47 | private String lastADBDevicesOutput; 48 | 49 | @Override 50 | public void start(Stage primaryStage) throws Exception { 51 | URL resource = getClass().getResource("/main.fxml"); 52 | FXMLLoader loader = new FXMLLoader(resource); 53 | loader.setController(this); 54 | Parent root = loader.load(); 55 | 56 | primaryStage.getIcons().addAll( 57 | new Image(Main.class.getResourceAsStream("/icons/ic_adbremoteconnect.png")), 58 | new Image(Main.class.getResourceAsStream("/icons/ic_adbremoteconnect@2x.png"))); 59 | primaryStage.setTitle("ADB WiFi Connect"); 60 | Scene scene = new Scene(root, 700, 640); 61 | scene.getStylesheets().add("/stylesheet.css"); 62 | primaryStage.iconifiedProperty().addListener(new ChangeListener() { 63 | @Override 64 | public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { 65 | if (newValue) { 66 | // not showing... 67 | isWindowActive = false; 68 | log.debug("Window not showing"); 69 | } else { 70 | // showing ... 71 | isWindowActive = true; 72 | startDeviceListUpdateThread(); 73 | log.debug("Window showing"); 74 | } 75 | } 76 | }); 77 | primaryStage.setOnCloseRequest(new EventHandler() { 78 | @Override 79 | public void handle(WindowEvent event) { 80 | isWindowActive = false; 81 | Platform.exit(); 82 | System.exit(0); 83 | } 84 | }); 85 | primaryStage.setScene(scene); 86 | primaryStage.show(); 87 | } 88 | 89 | public static void main(String[] args) { 90 | launch(args); 91 | } 92 | 93 | private void setupLogAppender() { 94 | onScreenConsoleOutputDelegate = new OnScreenConsoleOutputDelegate(outputTextArea); 95 | Logger.getRootLogger().addAppender(onScreenConsoleOutputDelegate); 96 | // Logger.getRootLogger().addAppender(new AlertOnErrorsDelegate()); 97 | } 98 | 99 | private void initializeSavedData() { 100 | // Retrieve the user preference node for the package 101 | Preferences systemRoot = Preferences.userRoot(); 102 | prefs = systemRoot.node(PREFS_ID); 103 | 104 | String jarLocation = prefs.get(PREF_JAR_LOCATION, null); 105 | if (jarLocation != null) { 106 | log.info("For launching outside of the IDE, find the runnable jar file here: \n" + jarLocation + "\n"); 107 | } 108 | 109 | adbPath = prefs.get(PREF_ADB_LOCATION, "adb"); 110 | if (adbPath == null) adbPath = "adb"; 111 | log.debug("ADB path: " + adbPath); 112 | 113 | String savedData = prefs.get(PREF_SAVED_CONNECTIONS, null); 114 | 115 | if (savedData != null) { 116 | Device[] deserializeArray = SerializeHelper.deserializeArray(savedData); 117 | if (deserializeArray != null) { 118 | observableListSavedConnections.addAll(new ArrayList<>(Arrays.asList(deserializeArray))); 119 | } 120 | 121 | if (observableListSavedConnections.size() == 0) { 122 | writeWelcome(false); 123 | } else { 124 | writeWelcome(true); 125 | } 126 | } 127 | } 128 | 129 | private void writeWelcome(boolean hasSavedConnections) { 130 | if (!hasSavedConnections) { 131 | log.info("Welcome to ADB WiFi Connect!\n\n" + 132 | "Please plug in an Android device by USB cable, make sure it is on the same WiFi as your computer and then click CONNECT to establish a remote connection to the device. A new connection will show up on the list. Click SAVE on the remote connection to add it to the list of saved connections and next time a remote connection is needed, simply click CONNECT on the saved connection.\n" + 133 | "\n" + 134 | "Happy developing! :)"); 135 | } else { 136 | log.info("Welcome back!\n\n" + 137 | "Please click CONNECT on a saved connection to reconnect to that, or plug in a new device with USB to make a new remote connection.\n" + 138 | "\n" + 139 | "Happy developing! :)\n"); 140 | } 141 | 142 | 143 | } 144 | 145 | //run from background thread 146 | private void updateListOfDevices(String[] adbDevicesListOutput) { 147 | ArrayList currentDevices = new ArrayList<>(); 148 | for (String adbDeviceLine : adbDevicesListOutput) { 149 | if (adbDeviceLine.contains("List") || adbDeviceLine.contains("daemon") || adbDeviceLine.trim().equals("")) { 150 | //ignore line 151 | } else { 152 | //is a device line so check for either IP or get device adb ID 153 | Device currentDevice = new Device(); 154 | if (adbDeviceLine.contains("offline")) { 155 | currentDevice.setType(Device.DEVICE_TYPE_OFFLINE); 156 | currentDevice.setName(adbDeviceLine.replace("offline", "").trim()); 157 | } else { 158 | String ipFromText = IPHelper.getIPFromText(adbDeviceLine); 159 | if (ipFromText == null) { 160 | currentDevice.setSerialID(adbDeviceLine.replace("device", "").trim()); 161 | currentDevice.setRemoteIP(ADBCommands.getDeviceIP(currentDevice)); 162 | currentDevice.setType(Device.DEVICE_TYPE_USB); 163 | } else { 164 | currentDevice.setRemoteIP(ipFromText); 165 | currentDevice.setSerialID(ADBCommands.getDeviceSerialNo(currentDevice)); 166 | currentDevice.setType(Device.DEVICE_TYPE_REMOTE); 167 | } 168 | currentDevice.setName(ADBCommands.getDeviceName(currentDevice)); 169 | } 170 | 171 | currentDevices.add(currentDevice); 172 | } 173 | } 174 | updateData(currentDevices); 175 | } 176 | 177 | private void startDeviceListUpdateThread() { 178 | Runnable r = new Runnable() { 179 | public void run() { 180 | log.debug("Running update of adb devices with window active: " + isWindowActive); 181 | String output = ADBCommands.runCommand(Main.adbPath + " devices"); 182 | log.debug(output); 183 | if (!output.equals(lastADBDevicesOutput)) { 184 | updateListOfDevices(output.split("\n")); 185 | lastADBDevicesOutput = output; 186 | } 187 | 188 | try { 189 | Thread.sleep(3000); 190 | if (isWindowActive) { 191 | new Thread(this).start(); 192 | } 193 | } catch (InterruptedException e) { 194 | e.printStackTrace(); 195 | log.error(e.getMessage()); 196 | } 197 | 198 | } 199 | }; 200 | 201 | log.debug("Starting update thread for list of ADB devices"); 202 | new Thread(r).start(); 203 | } 204 | 205 | public void updateData(List devices) { 206 | Platform.runLater(new Runnable() { 207 | @Override 208 | public void run() { 209 | // Update UI here. 210 | observableList.clear(); 211 | observableList.addAll(devices); 212 | //TODO update list of saved devices (in case there's an overlap) 213 | listViewSaved.refresh(); 214 | } 215 | }); 216 | } 217 | 218 | private void updateSavedConnectionsList() { 219 | //refresh list of current connections since list of saved connections have changed data 220 | listView.refresh(); 221 | 222 | //saved the data to remember for future launches 223 | prefs.put(PREF_SAVED_CONNECTIONS, SerializeHelper.serializeArray(observableListSavedConnections.toArray(new Device[observableListSavedConnections.size()]))); 224 | try { 225 | prefs.flush(); 226 | } catch (BackingStoreException e) { 227 | e.printStackTrace(); 228 | } 229 | } 230 | 231 | public boolean hasRemoteIPSaved(String remoteIP) { 232 | for (Device savedDevice : observableListSavedConnections) { 233 | if (remoteIP.equals(savedDevice.getRemoteIP())) { 234 | return true; 235 | } 236 | } 237 | return false; 238 | } 239 | 240 | public boolean isCurrentlyConnectedToRemoteIP(String remoteIP) { 241 | for (Device currentConnectedDevice : observableList) { 242 | if (currentConnectedDevice.type == Device.DEVICE_TYPE_REMOTE && remoteIP.equals(currentConnectedDevice.getRemoteIP())) { 243 | return true; 244 | } 245 | } 246 | return false; 247 | } 248 | 249 | public void saveConnection(Device device) { 250 | Platform.runLater(new Runnable() { 251 | @Override 252 | public void run() { 253 | for (Device savedDevice : observableListSavedConnections) { 254 | if (device.getRemoteIP().equals(savedDevice.getRemoteIP())) { 255 | observableListSavedConnections.remove(savedDevice); 256 | break; 257 | } 258 | } 259 | 260 | Device newSavedConnection = new Device(); 261 | newSavedConnection.setName(device.getName()); 262 | newSavedConnection.setRemoteIP(device.getRemoteIP()); 263 | newSavedConnection.setSerialID(device.getSerialID()); 264 | newSavedConnection.setType(Device.DEVICE_TYPE_SAVED_REMOTE); 265 | observableListSavedConnections.add(0, newSavedConnection); 266 | 267 | updateSavedConnectionsList(); 268 | } 269 | }); 270 | } 271 | 272 | public void deleteConnection(Device device) { 273 | Platform.runLater(new Runnable() { 274 | @Override 275 | public void run() { 276 | for (Device savedDevice : observableListSavedConnections) { 277 | if (device.getRemoteIP().equals(savedDevice.getRemoteIP())) { 278 | observableListSavedConnections.remove(savedDevice); 279 | updateSavedConnectionsList(); 280 | return; 281 | } 282 | } 283 | } 284 | }); 285 | } 286 | 287 | public void setupListView() { 288 | listView.setSelectionModel(new TableSelectionModel() { 289 | @Override 290 | public boolean isSelected(int row, TableColumnBase column) { 291 | return false; 292 | } 293 | 294 | @Override 295 | public void select(int row, TableColumnBase column) { 296 | 297 | } 298 | 299 | @Override 300 | public void clearAndSelect(int row, TableColumnBase column) { 301 | 302 | } 303 | 304 | @Override 305 | public void clearSelection(int row, TableColumnBase column) { 306 | 307 | } 308 | 309 | @Override 310 | public void selectLeftCell() { 311 | 312 | } 313 | 314 | @Override 315 | public void selectRightCell() { 316 | 317 | } 318 | 319 | @Override 320 | public void selectAboveCell() { 321 | 322 | } 323 | 324 | @Override 325 | public void selectBelowCell() { 326 | 327 | } 328 | 329 | @Override 330 | public void selectRange(int minRow, TableColumnBase minColumn, int maxRow, TableColumnBase maxColumn) { 331 | 332 | } 333 | 334 | @Override 335 | protected int getItemCount() { 336 | return 0; 337 | } 338 | 339 | @Override 340 | protected Object getModelItem(int index) { 341 | return null; 342 | } 343 | 344 | @Override 345 | protected void focus(int index) { 346 | 347 | } 348 | 349 | @Override 350 | protected int getFocusedIndex() { 351 | return 0; 352 | } 353 | }); 354 | listView.setItems(observableList); 355 | listView.setCellFactory(new Callback, ListCell>() { 356 | @Override 357 | public ListCell call(ListView listView) { 358 | return new ListViewCell(Main.this); 359 | } 360 | }); 361 | 362 | listViewSaved.setSelectionModel(new TableSelectionModel() { 363 | @Override 364 | public boolean isSelected(int row, TableColumnBase column) { 365 | return false; 366 | } 367 | 368 | @Override 369 | public void select(int row, TableColumnBase column) { 370 | 371 | } 372 | 373 | @Override 374 | public void clearAndSelect(int row, TableColumnBase column) { 375 | 376 | } 377 | 378 | @Override 379 | public void clearSelection(int row, TableColumnBase column) { 380 | 381 | } 382 | 383 | @Override 384 | public void selectLeftCell() { 385 | 386 | } 387 | 388 | @Override 389 | public void selectRightCell() { 390 | 391 | } 392 | 393 | @Override 394 | public void selectAboveCell() { 395 | 396 | } 397 | 398 | @Override 399 | public void selectBelowCell() { 400 | 401 | } 402 | 403 | @Override 404 | public void selectRange(int minRow, TableColumnBase minColumn, int maxRow, TableColumnBase maxColumn) { 405 | 406 | } 407 | 408 | @Override 409 | protected int getItemCount() { 410 | return 0; 411 | } 412 | 413 | @Override 414 | protected Object getModelItem(int index) { 415 | return null; 416 | } 417 | 418 | @Override 419 | protected void focus(int index) { 420 | 421 | } 422 | 423 | @Override 424 | protected int getFocusedIndex() { 425 | return 0; 426 | } 427 | }); 428 | listViewSaved.setItems(observableListSavedConnections); 429 | listViewSaved.setCellFactory(new Callback, ListCell>() { 430 | @Override 431 | public ListCell call(ListView listView) { 432 | return new ListViewCell(Main.this); 433 | } 434 | }); 435 | } 436 | 437 | @Override 438 | public void initialize(URL location, ResourceBundle resources) { 439 | assert listView != null : "fx:id=\"listView\" was not injected: check your FXML file."; 440 | setupListView(); 441 | setupLogAppender(); 442 | initializeSavedData(); 443 | startDeviceListUpdateThread(); 444 | 445 | menuItemDebug.setOnAction(new EventHandler() { 446 | @Override 447 | public void handle(ActionEvent actionEvent) { 448 | onScreenConsoleOutputDelegate.setShowDebug(menuItemDebug.isSelected()); 449 | } 450 | }); 451 | } 452 | 453 | 454 | } 455 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/java/dk/appdictive/adbwificonnect/OnScreenConsoleOutputDelegate.java: -------------------------------------------------------------------------------- 1 | package dk.appdictive.adbwificonnect; 2 | 3 | import javafx.scene.control.TextArea; 4 | import org.apache.log4j.AppenderSkeleton; 5 | import org.apache.log4j.spi.LoggingEvent; 6 | 7 | /** 8 | * Created by tobalr on 3/1/17. 9 | */ 10 | public class OnScreenConsoleOutputDelegate extends AppenderSkeleton { 11 | public static final int MAX_LOG_LENGTH = 5000; 12 | private TextArea outputTextArea; 13 | private boolean showDebug = false; 14 | String fullLog = "", noDebugLog = ""; 15 | 16 | public OnScreenConsoleOutputDelegate(TextArea outputTextArea) { 17 | this.outputTextArea = outputTextArea; 18 | } 19 | 20 | @Override 21 | protected void append(LoggingEvent loggingEvent) { 22 | String message = String.format("[%s] %s", loggingEvent.getLevel(), loggingEvent.getRenderedMessage()); 23 | 24 | if (loggingEvent.getLevel().equals(org.apache.log4j.Level.DEBUG)) { 25 | if (showDebug) addToLogOutput(message); 26 | } else { 27 | addToLogOutput(message); 28 | noDebugLog = noDebugLog.substring(Math.max(0, noDebugLog.length() - MAX_LOG_LENGTH), noDebugLog.length()); 29 | noDebugLog += message + "\n"; 30 | } 31 | 32 | fullLog = fullLog.substring(Math.max(0, fullLog.length() - MAX_LOG_LENGTH), fullLog.length()); 33 | fullLog += message + "\n"; 34 | } 35 | 36 | 37 | @Override 38 | public void close() { 39 | addToLogOutput("--Closing--"); 40 | } 41 | 42 | @Override 43 | public boolean requiresLayout() { 44 | return false; 45 | } 46 | 47 | private void addToLogOutput(String message) { 48 | int length = outputTextArea.getText().length(); 49 | outputTextArea.deleteText(0, Math.max(0, length - MAX_LOG_LENGTH)); 50 | outputTextArea.appendText(message + "\n"); 51 | } 52 | 53 | public void setShowDebug(boolean showDebug) { 54 | this.showDebug = showDebug; 55 | if (showDebug) { 56 | outputTextArea.setText(fullLog); 57 | } else { 58 | outputTextArea.setText(noDebugLog); 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/java/dk/appdictive/adbwificonnect/SerializeHelper.java: -------------------------------------------------------------------------------- 1 | package dk.appdictive.adbwificonnect; 2 | 3 | import java.io.*; 4 | import java.util.Base64; 5 | 6 | /** 7 | * Created by JesperLR on 21-May-17. 8 | */ 9 | public class SerializeHelper { 10 | 11 | public static String serializeArray(Device[] array) { 12 | // serialize 13 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 14 | try { 15 | new ObjectOutputStream(out).writeObject(array); 16 | } catch (IOException e) { 17 | e.printStackTrace(); 18 | } 19 | 20 | // your string 21 | String serializedArray = new String(Base64.getEncoder().encode(out.toByteArray())); 22 | return serializedArray; 23 | } 24 | 25 | public static Device[] deserializeArray(String serializedArray) { 26 | // deserialize 27 | try { 28 | ByteArrayInputStream in = new ByteArrayInputStream(Base64.getDecoder().decode(serializedArray.getBytes())); 29 | return (Device[]) new ObjectInputStream(in).readObject(); 30 | } catch (IOException e) { 31 | e.printStackTrace(); 32 | } catch (ClassNotFoundException e) { 33 | e.printStackTrace(); 34 | } catch (IllegalArgumentException e) { 35 | e.printStackTrace(); 36 | } catch (ClassCastException cce) { 37 | cce.printStackTrace(); 38 | } 39 | return null; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/resources/icons/ic_adbremoteconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdictive/ADBWiFiConnect/cca0aa79753c1f5a8e050300149a85b802722c0b/ADBWiFiConnectGUI/src/main/resources/icons/ic_adbremoteconnect.png -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/resources/icons/ic_adbremoteconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appdictive/ADBWiFiConnect/cca0aa79753c1f5a8e050300149a85b802722c0b/ADBWiFiConnectGUI/src/main/resources/icons/ic_adbremoteconnect@2x.png -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/resources/list_cell_item.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 26 | 27 | -------------------------------------------------------------------------------- /ADBWiFiConnectGUI/src/main/resources/main.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 |