├── .gitignore
├── .idea
├── ant.xml
├── artifacts
│ └── JavaFXApp.xml
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── description.html
├── encodings.xml
├── gradle.xml
├── libraries
│ └── gson_2_2_4.xml
├── misc.xml
├── modules.xml
├── uiDesigner.xml
├── vcs.xml
└── workspace.xml
├── ADBUITool.iml
├── README.md
├── adbuitool.properties
├── adbuitool.xml
├── libs
└── gson-2.2.4.jar
├── module_adbuitool.xml
└── src
├── application
├── ADBHelper.java
├── AdbUtils.java
├── DateUtil.java
├── DialogUtil.java
├── FXMLMain.fxml
├── FXMLMainController.java
├── FileUtils.java
├── FolderUtil.java
├── Main.java
├── ProcessUtils.java
├── apks
│ ├── APKsTab.fxml
│ └── APKsTabController.java
├── application.css
├── applications
│ ├── ApplicationsTab.fxml
│ └── ApplicationsTabController.java
├── batchcommands
│ ├── BatchCommandEditController.java
│ ├── BatchCommandEditLayout.fxml
│ ├── BatchCommandTab.fxml
│ ├── BatchCommandTabController.java
│ ├── BatchCommandViewUtil.java
│ ├── BatchCommandsListView.java
│ ├── CommandWizardController.java
│ └── CommandWizardLayout.fxml
├── devices
│ ├── DevicesController.java
│ └── DevicesLayout.fxml
├── intentbroadcasts
│ ├── IntentBroadcast.java
│ ├── IntentBroadcastsController.java
│ └── IntentsBroadcastsLayout.fxml
├── log
│ └── Logger.java
├── logexceptions
│ ├── ExceptionLog.java
│ ├── Log.java
│ ├── LogExceptionsController.java
│ └── LogExceptionsLayout.fxml
├── model
│ ├── Application.java
│ ├── Command.java
│ ├── CommandBatch.java
│ ├── Device.java
│ ├── Model.java
│ ├── ModelListener.java
│ └── PackageProcess.java
├── preferences
│ ├── PreferenceController.java
│ ├── PreferenceLayout.fxml
│ └── Preferences.java
├── screencapture
│ ├── ScreenCaptureController.java
│ └── ScreenCaptureLayout.fxml
├── services
│ ├── ApplicationsMonitorService.java
│ └── DeviceMonitorService.java
├── startupcheck
│ ├── StartupCheckController.java
│ └── StartupCheckLayout.fxml
├── terminal
│ ├── TerminalController.java
│ └── TerminalLayout.fxml
└── view
│ ├── DateTimePicker.fxml
│ └── DateTimePickerController.java
└── res
└── icon.png
/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yapplications/ADB-GUI/9aeee11d24ff0fa290ac91d39ccca7852bd3f849/.gitignore
--------------------------------------------------------------------------------
/.idea/ant.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/artifacts/JavaFXApp.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | $PROJECT_DIR$/out/artifacts/JavaFXApp
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/description.html:
--------------------------------------------------------------------------------
1 | Simple JavaFX 2.0 application that includes simple .fxml file with attached controller and Main class to quick start. Artifact to build JavaFX application is provided.
2 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/libraries/gson_2_2_4.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ADBUITool.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ADB-GUI - [GitHub Page](https://yapplications.github.io/ADB-GUI/)
2 | UI wrapper around ADB to make life easier for Android Developer and QA
3 |
4 | [Medium post about ADB-GUI-Tool (27.01.2017)](https://medium.com/@evgenishafran/keep-your-sanity-while-doing-android-development-1d9964929199#.k7s9ztavo)
5 |
6 | 
7 |
8 | [Install](https://github.com/yapplications/ADB-GUI/blob/master/README.md#install)
9 | [Submit an issue](https://github.com/yapplications/ADB-GUI/blob/master/README.md#submit-an-issue)
10 | [Use Cases](https://github.com/yapplications/ADB-GUI/blob/master/README.md#use-cases)
11 | [Abilities](https://github.com/yapplications/ADB-GUI/blob/master/README.md#abilities)
12 | [Share 'command batch' / 'intent' with your team](https://github.com/yapplications/ADB-GUI/blob/master/README.md#share-command-batch--intent-with-your-team)
13 | [Known Isseus](https://github.com/yapplications/ADB-GUI/blob/master/README.md#known-issues)
14 |
15 | ## Install:
16 |
17 | * You should have Java and adb (comes with android studio) installed.
18 |
19 | 1. Dowload: [ADB-GUI-Tool 0.1.9v](https://github.com/yapplications/ADB-GUI/releases/download/0.1.9v/ADB-GUI-Tool-0.1.9.zip)
20 |
21 | 2. Unzip to a path **without spaces**
22 |
23 | 3. MAC: give running permission to: ADB-GUI-Tool.jar
24 |
25 | 4. Run: ADB-GUI-Tool.jar (Double press from finder / explorer or run: `java -jar ADB-GUI-Tool.jar` from terminal)
26 |
27 | 5. On first run: change 'adb path' in preference screen to point to your local adb
28 |
29 | ## Submit an issue
30 | If you encountered a problem, something is not working, please open an issue.
31 | But before you do so please go to the app path, open 'preference' file,
32 | Change: "debug":false -> "debug":true
33 | Try to recreate your error and send me the last logs (both the error and regular one) created at: 'app-logs' directory under you app directory.
34 |
35 | ## Use Cases:
36 |
37 | 1. Automate login forms via Batch Commands
38 | 2. Automate device readiness for QA (install multiple APKs and copy files) via Batch Commands
39 | 3. Take snapshots easily
40 | 4. Install APKs
41 | 5. Test deep linking via Inten / Broadcasts
42 | 6. Get APKs from device
43 | 7. Run monkey runner with a simple click
44 |
45 | And much more...
46 |
47 | ## Abilities:
48 |
49 | The app devided into Device Panel 7 Tabs and status line
50 |
51 | #### Status line:
52 |
53 | Appears at the bottom, most of the commands will update it.
54 |
55 | * Black color text: command is running
56 | * Green color text: command executed succesfully
57 | * Red color text: command failed
58 |
59 | Some commands will appear green result even if the command failed
60 |
61 | #### Device Panel:
62 |
63 | 1. Device selection: all device commands that you will execute will be executed on the device you select here
64 |
65 | 2. Connect device via WiFi: click in order to start working via WiFi
66 | * Device should be on the same network as the pc
67 | * After pressed there should be an extra device in the list and you can disconnect it from usb.
68 | * Connection will be lost if adb / device restarts
69 |
70 | 3. Take snapshot: opens an 'device view' screen
71 | * Screen will be updated by itself
72 | * Press 'Save' to save a snapshot
73 | * You can save as many snapshots as you like in one session
74 |
75 | 4. Send quick text to device: enter the text you want to send and press enter
76 |
77 | 5. Change emulator date: opens a dilog for changing emulator time
78 | * Works only on emulators
79 | * Will also disable the auto date / time zone update
80 | * If you move to the clock extensively it can jump back
81 |
82 | 6. Open developer settings on the device
83 |
84 | 7. Open app directory: open the directory where all the files stored on your pc
85 |
86 | #### Tabs
87 |
88 | 1. Batch command
89 | * Create and execute batch adb commands
90 | * You don't need to know the adb command behind it, just use the command wizard tool
91 |
92 | 2. Applications
93 | * Clear app data
94 | * Uninstall app
95 | * Kill all apps process, only on debugble apps, to emulate android memory clean
96 | * Get installed APK from device
97 | * Run monkey runner on any app
98 |
99 | 3. APKs
100 | * Install APK for pc (can configure to show several folders)
101 | * Run de-obfuscation tool (need to be downloaded separately)
102 |
103 | 4. Intent / Broadcasts
104 | * Send broadcast / intents to device
105 | * Save you popular ones for future use
106 |
107 | 5. Log / Exceptions
108 | * See logcat
109 | * Browse through exceptions
110 | * Save exception / log to file
111 |
112 | 6. Terminal
113 | * Expirement with adb commands
114 |
115 | 7. Preference
116 | * Edit app preference
117 |
118 | ## Share 'command batch' / 'intent' with your team
119 | You can easily send you team an 'command batch' or 'intent' they both saved in a JSON file.
120 | Just find the right file under your app directory and send it to your team mate.
121 | Ask him to put it into the right directory and that it, he got it!
122 |
123 | ## Known Issues
124 |
125 | Was developed and tested on MAC, windows and linux support is limited some features may not work (It should but was partialy tested).
126 | Was tested on variety of devices mainly on NEXUS 5x and emulators
127 |
128 | 1. Unothorized device: some times the adb devices command will retrive one of the devices as unothorized in this case you should:
129 | * Press "Kill ADB"
130 | * Open terminal / command line and write "adb devices" (Running this command from the App not working for some reason)
131 | * Press "Start monitoring" to get the device list back
132 |
133 | 2. Some of the functions will not work if the app is placed in path with spaces
134 |
--------------------------------------------------------------------------------
/adbuitool.properties:
--------------------------------------------------------------------------------
1 | path.variable.kotlin_bundled=/Applications/IntelliJ IDEA CE.app/Contents/plugins/Kotlin/kotlinc
2 | path.variable.maven_repository=/Users/evgeni.shafran/.m2/repository
3 | jdk.home.1.8=/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home
4 | idea.home=/Applications/IntelliJ IDEA CE.app/Contents
5 | javac2.instrumentation.includeJavaRuntime=false
--------------------------------------------------------------------------------
/adbuitool.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/libs/gson-2.2.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yapplications/ADB-GUI/9aeee11d24ff0fa290ac91d39ccca7852bd3f849/libs/gson-2.2.4.jar
--------------------------------------------------------------------------------
/module_adbuitool.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/application/ADBHelper.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import application.intentbroadcasts.IntentBroadcast;
4 | import application.log.Logger;
5 |
6 | import java.text.SimpleDateFormat;
7 | import java.util.Calendar;
8 | import java.util.HashSet;
9 | import java.util.Set;
10 |
11 | public class ADBHelper {
12 | public static boolean pull(String from, String to) {
13 |
14 | //to = to.replaceAll(" ", "_");
15 | //to = to.replaceAll(" ", "\\\\ ");
16 | /*byte ptext[] = new byte[0];
17 | try {
18 | ptext = to.getBytes("UTF-8");
19 |
20 | to = new String(ptext, "CP1252");
21 | } catch (UnsupportedEncodingException e) {
22 | e.printStackTrace();
23 | }*/
24 |
25 | String run ="pull " + from + " " + to + "";
26 | String result = AdbUtils.run(run);
27 |
28 | if (!result.trim().contains("100%")) {
29 | Logger.e(result + "\nto: " + to);
30 | return false;
31 | }
32 |
33 | return true;
34 | }
35 |
36 | public static String rm(String fileToDelete) {
37 | return AdbUtils.run("shell rm \"" + fileToDelete + "\"");
38 | }
39 |
40 | public static String killServer() {
41 | return AdbUtils.run("kill-server");
42 | }
43 |
44 | public static String openApp(String packageString) {
45 | String result = AdbUtils.run("shell monkey -p " + packageString + " 1");
46 |
47 | if (!result.contains("No activities found to run")) {
48 | result = null;
49 | }
50 |
51 | return result;
52 | }
53 |
54 | public static String runMonkey(String applicationName, int numberOfSteps, int throttle) {
55 | String result = AdbUtils.run("shell monkey -p " + applicationName + " -v --throttle " + throttle + " " + numberOfSteps);
56 |
57 | if (!result.contains("No activities found to run")) {
58 | result = null;
59 | }
60 |
61 | return result;
62 | }
63 |
64 | public static String kill(String packageName, String pid) {
65 | String result = AdbUtils.run("shell run-as " + packageName + " kill " + pid);
66 |
67 | if (!result.contains("not debuggable")) {
68 | result = null;
69 | }
70 |
71 |
72 | return result;
73 | }
74 |
75 | public static String clearData(String getSelectedAppPackage) {
76 |
77 | String result = AdbUtils.run("shell pm clear " + getSelectedAppPackage);
78 |
79 | if (result.contains("Success")) {
80 | result = null;
81 | }
82 |
83 | return result;
84 | }
85 |
86 | public static String install(String selectedApk) {
87 |
88 | String result = AdbUtils.run("install -r " + selectedApk + "");
89 | String[] split = result.split("\n");
90 |
91 | if (split.length > 0) {
92 | if (split[split.length - 1].contains("Failure") || split[split.length - 1].contains("Missing")) {
93 | return split[split.length - 1];
94 | } else {
95 | return null;
96 | }
97 | }
98 |
99 | return "Wired Error";
100 | }
101 |
102 | public static void sendInputText(String text) {
103 | String result = AdbUtils.run("shell input text \"" + text + "\"");
104 | Logger.d("shell input text " + text + " -> " + result);
105 |
106 | }
107 |
108 | public static String sendIntent(IntentBroadcast intentBroadcast) {
109 | String command = "shell am " + intentBroadcast.activityManagerCommand +
110 | (!intentBroadcast.action.equals("") ? " -a " + intentBroadcast.action : "") +
111 | (!intentBroadcast.data.equals("") ? " -d " + intentBroadcast.data : "") +
112 | (!intentBroadcast.mimeType.equals("") ? " -t " + intentBroadcast.mimeType : "") +
113 | (!intentBroadcast.category.equals("") ? " -c " + intentBroadcast.category : "") +
114 | (!intentBroadcast.component.equals("") ? " -n " + intentBroadcast.component : "") +
115 | "";
116 |
117 | Logger.d(command);
118 |
119 | String result = AdbUtils.run(command);
120 |
121 | if (!result.contains("Error:")) {
122 | result = null;
123 | }
124 |
125 | return result;
126 | }
127 |
128 | public static String connectDeviceToWifi() {
129 | String result = null;
130 |
131 | result = AdbUtils.run("shell ip -f inet addr show wlan0");
132 | Logger.d("shell ip " + result);
133 |
134 | String[] split = result.split("\n");
135 | String ip = null;
136 | for (String line : split) {
137 | if (line.contains("inet")) {
138 |
139 | String[] splitSpaces = line.split("\\s+");
140 | int i = 0;
141 | for (String word : splitSpaces) {
142 | if (word.contains("inet")) {
143 | break;
144 | }
145 | i++;
146 | }
147 |
148 | if (splitSpaces.length > i + 1) {
149 | ip = splitSpaces[i + 1].split("/")[0];
150 | }
151 |
152 | Logger.d("connectDeviceToWifi: ip: " + ip);
153 |
154 | break;
155 | }
156 | }
157 |
158 | if (ip != null) {
159 | result = AdbUtils.run("tcpip 5555");
160 | Logger.d("tcpip " + result);
161 | if (result.contains("restarting")) {
162 |
163 | result = AdbUtils.run("connect " + ip + ":5555");
164 | Logger.d("connect " + result);
165 |
166 | if (result.contains("connected")) {
167 | result = null;
168 | }
169 |
170 | }
171 | }
172 | return result;
173 | }
174 |
175 | public static String setDateTime(Calendar calendar) {
176 | String result = null;
177 | result = AdbUtils.run("shell settings put global auto_time 0");
178 | Logger.d(result);
179 |
180 | result = AdbUtils.run("shell settings put global auto_time_zone 0");
181 | Logger.d(result);
182 |
183 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMddhhmmyyyy.ss");
184 |
185 | String dateString = simpleDateFormat.format(calendar.getTime());
186 |
187 | result = AdbUtils.run("root");
188 |
189 | result = AdbUtils.run("shell date " + dateString);
190 | Logger.d("shell date " + dateString + " ---> " + result);
191 |
192 | if (!result.contains("bad date") && !result.contains("not permitted")){
193 | result = null;
194 | }
195 |
196 | return result;
197 | }
198 |
199 | public static Set getPackages() {
200 | String result = AdbUtils.run("shell pm list packages");
201 |
202 | String[] split = result.split("\n");
203 |
204 | Set packages = new HashSet();
205 |
206 | for (int i = 1; i < split.length; i++) {
207 | String packageName = split[i].replace("package:", "").trim();
208 | if (packageName.equals("android")) {
209 | continue;
210 | }
211 |
212 | packages.add(packageName);
213 | }
214 |
215 | return packages;
216 | }
217 |
218 | public static boolean isADBFound() {
219 | String result = AdbUtils.run("version");
220 |
221 | return result.startsWith("Android Debug Bridge");
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/src/application/AdbUtils.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.InputStreamReader;
5 | import java.util.concurrent.*;
6 |
7 | import application.preferences.Preferences;
8 | import javafx.application.Platform;
9 | import application.log.Logger;
10 | import application.model.Command;
11 | import application.model.CommandBatch;
12 | import application.model.Device;
13 | import application.model.Model;
14 |
15 | public class AdbUtils {
16 | //public static ExecutorService executor = Executors.newSingleThreadExecutor();
17 | public static ExecutorService executor = Executors.newScheduledThreadPool(2);
18 | public static ExecutorService executorTimeout = Executors.newScheduledThreadPool(2);
19 |
20 | public interface TimeCallListener{
21 | void timeout();
22 | void error(Exception e);
23 | }
24 |
25 | public static T timedCall(Callable c, long timeout, TimeUnit timeUnit, final TimeCallListener timeCallListener) {
26 | FutureTask task = new FutureTask(c);
27 | executor.execute(task);
28 | executorTimeout.execute(new Runnable() {
29 | @Override
30 | public void run() {
31 | try {
32 | task.get(timeout, timeUnit);
33 | } catch (InterruptedException e) {
34 | timeCallListener.error(e);
35 | } catch (ExecutionException e) {
36 | timeCallListener.error(e);
37 | } catch (TimeoutException e) {
38 | timeCallListener.timeout();
39 | }
40 | }
41 | });
42 | return null;
43 | }
44 |
45 |
46 | public static String run(CommandBatch batch) {
47 | String result = "";
48 |
49 | for (Command command : batch.commands) {
50 | result += executeADBCommand(command.command) + "\n";
51 | }
52 |
53 | return result;
54 | }
55 |
56 | private static String executeADBCommand(String command) {
57 | return executeCommand(getAdbCommand(command));
58 | }
59 |
60 | public static String getAdbCommand(String command) {
61 | if (command.startsWith("adb")){
62 | command = command.replaceFirst("adb", "");
63 | }
64 |
65 | Device selectedDevice = Model.instance.getSelectedDevice();
66 | return Preferences.getInstance().getAdbInstallLocatoin() + "adb "
67 | + (selectedDevice != null ? "-s " + selectedDevice.getId() + " " : "") + command;
68 | // return "adb " + command;
69 | }
70 |
71 | public interface ADBRunListener {
72 | void onFinish(String resporse);
73 | }
74 |
75 | public static void runAsync(String string, ADBRunListener listener) {
76 | executor.execute(new Runnable() {
77 |
78 | @Override
79 | public void run() {
80 | String resporse = executeADBCommand(string);
81 |
82 | if (listener != null) {
83 | Platform.runLater(new Runnable() {
84 | @Override
85 | public void run() {
86 | listener.onFinish(resporse);
87 | }
88 | });
89 | }
90 | }
91 | });
92 | }
93 |
94 | public static String run(Command command) {
95 |
96 | return executeADBCommand(command.command);
97 | }
98 |
99 | public static String run(String string) {
100 |
101 | return executeADBCommand(string);
102 | }
103 |
104 | public static String executeCommand(String [] command) {
105 | return executeCommand(null, command);
106 | }
107 |
108 | public static String executeCommand(String command) {
109 | return executeCommand(command, null);
110 | }
111 | public static String executeCommand(String command , String [] commands) {
112 | // System.out.println("Run: " + command);
113 |
114 | StringBuffer output = new StringBuffer();
115 |
116 | Process p;
117 | try {
118 |
119 | String[] envp = {};
120 | if (commands == null) {
121 | p = Runtime.getRuntime().exec(command, envp);
122 | } else {
123 | p = Runtime.getRuntime().exec(commands, envp);
124 | }
125 |
126 | p.waitFor(10, TimeUnit.SECONDS);
127 | BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
128 |
129 | boolean firstLine = true;
130 | String line = "";
131 | while ((line = reader.readLine()) != null) {
132 | if (!firstLine) {
133 | output.append("\n");
134 | }
135 | firstLine = false;
136 | output.append(line);
137 | // System.out.println(line);
138 | }
139 |
140 | reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
141 |
142 | while ((line = reader.readLine()) != null) {
143 | if (!firstLine) {
144 | output.append("\n");
145 | }
146 | firstLine = false;
147 | output.append(line + "\n");
148 | // System.out.println(line);
149 | }
150 |
151 | } catch (Exception e) {
152 | e.printStackTrace();
153 | output.append(e.getMessage());
154 | }
155 |
156 | String result = output.toString();
157 |
158 | if (false){
159 | Logger.d("Run: " + command + "\n" + result);
160 | }
161 |
162 | return result;
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/src/application/DateUtil.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Date;
5 |
6 | /**
7 | * Created by evgeni.shafran on 10/13/16.
8 | */
9 | public class DateUtil {
10 |
11 | public static String getCurrentTimeStamp() {
12 | return new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date());
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/application/DialogUtil.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import javafx.scene.control.Alert;
4 | import javafx.scene.control.Alert.AlertType;
5 |
6 | public class DialogUtil {
7 | public static void showErrorDialog(String headerText){
8 | Alert alert = new Alert(AlertType.ERROR);
9 | alert.setTitle("Error");
10 | alert.setHeaderText(headerText);
11 | //alert.setContentText("Ooops, there was an error!");
12 |
13 | alert.showAndWait();
14 | }
15 |
16 | public static void showInfoDialog(String headerText) {
17 | Alert alert = new Alert(AlertType.INFORMATION);
18 | alert.setTitle("Info");
19 | alert.setHeaderText(headerText);
20 | //alert.setContentText("Ooops, there was an error!");
21 |
22 | alert.showAndWait();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/application/FXMLMain.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/application/FXMLMainController.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import java.awt.*;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.net.URL;
7 | import java.nio.file.Paths;
8 | import java.util.ResourceBundle;
9 |
10 | import application.apks.APKsTabController;
11 | import application.applications.ApplicationsTabController;
12 | import application.model.ModelListener;
13 | import application.preferences.Preferences;
14 | import application.screencapture.ScreenCaptureController;
15 | import application.startupcheck.StartupCheckController;
16 | import com.sun.prism.PresentableState;
17 | import javafx.application.Platform;
18 | import javafx.beans.value.ChangeListener;
19 | import javafx.beans.value.ObservableValue;
20 | import javafx.event.ActionEvent;
21 | import javafx.fxml.FXML;
22 | import javafx.fxml.FXMLLoader;
23 | import javafx.fxml.Initializable;
24 | import javafx.scene.Parent;
25 | import javafx.scene.control.*;
26 | import javafx.scene.control.Button;
27 | import javafx.scene.control.Label;
28 | import javafx.scene.paint.Color;
29 | import application.log.Logger;
30 | import application.log.Logger.LoggerListener;
31 | import javafx.stage.Stage;
32 |
33 | public class FXMLMainController implements Initializable {
34 |
35 | public static final int TAB_INDEX_APPLICATIONS = 1;
36 |
37 | protected static final int TAB_INDEX_APKS = 2;
38 | public Button buttonToggleEdit;
39 | public CheckBox checkBoxAlwaysOnTop;
40 |
41 | @FXML
42 | private TabPane tabPane;
43 |
44 | @FXML
45 | private Tab preferenceTabPage;
46 |
47 | @FXML
48 | private ApplicationsTabController applicationTabPageController;
49 |
50 | @FXML
51 | private APKsTabController apksTabPageController;
52 |
53 | @FXML
54 | private Label labelRunningLog;
55 | private Stage stage;
56 |
57 | @Override
58 | public void initialize(URL location, ResourceBundle resources) {
59 |
60 | if (Preferences.getInstance().isFirstRun()) {
61 |
62 | tabPane.getSelectionModel().select(preferenceTabPage);
63 | Preferences.getInstance().setFirstRun(false);
64 | try {
65 | Preferences.getInstance().save();
66 | } catch (IOException e) {
67 | e.printStackTrace();
68 | }
69 | }
70 |
71 | tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener() {
72 |
73 | @Override
74 | public void changed(ObservableValue extends Number> observable, Number oldValue, Number newValue) {
75 | if (oldValue.equals(TAB_INDEX_APPLICATIONS)) {
76 | applicationTabPageController.stopWorking();
77 | } else if (newValue.equals(TAB_INDEX_APPLICATIONS)) {
78 | applicationTabPageController.startWorking();
79 | }
80 |
81 | if (newValue.equals(TAB_INDEX_APKS)) {
82 | apksTabPageController.refreshList();
83 | }
84 | }
85 | });
86 |
87 | Logger.setShowLogListener(new LoggerListener() {
88 | @Override
89 | public void onNewLogToShow(String message) {
90 | log(Color.BLACK, message);
91 | }
92 |
93 | @Override
94 | public void onNewErrorLogToShow(String message) {
95 | log(Color.RED, message);
96 | }
97 |
98 | @Override
99 | public void onFinishLogToShow(String message) {
100 | log(Color.GREEN, message);
101 | }
102 | });
103 |
104 | Platform.runLater(new Runnable() {
105 | @Override
106 | public void run() {
107 | checkBoxAlwaysOnTop.setSelected(Preferences.getInstance().isWindowIsAlwaysOn());
108 | handleAlwaysOnTop(null);
109 |
110 | if (!Preferences.getInstance().
111 | isEditWindowIsOpen()) {
112 | handleToggleMainView(null);
113 | }
114 | }
115 | });
116 |
117 | openADBValidator();
118 | }
119 |
120 | private void openADBValidator() {
121 | Platform.runLater(new Runnable() {
122 | @Override
123 | public void run() {
124 |
125 |
126 | try {
127 | StartupCheckController.showScreen(getClass());
128 | } catch (IOException e) {
129 | e.printStackTrace();
130 | }
131 | }
132 | });
133 | }
134 |
135 | protected void log(Color color, String message) {
136 | labelRunningLog.setTextFill(color);
137 | labelRunningLog.setText(message);
138 | }
139 |
140 | protected ApplicationsTabController getApplicationController() {
141 |
142 | FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("applications/ApplicationsTab.fxml"));
143 |
144 | try {
145 | Parent root1 = (Parent) fxmlLoader.load();
146 | } catch (IOException e) {
147 | e.printStackTrace();
148 | }
149 | ApplicationsTabController controller = fxmlLoader.getController();
150 |
151 | return controller;
152 | }
153 |
154 |
155 | public void handleToggleMainView(ActionEvent actionEvent) {
156 |
157 | if (stage != null && tabPane != null) {
158 |
159 | Logger.d("tabPane " + tabPane);
160 |
161 | if (tabPane.isVisible()) {
162 | tabPane.setVisible(false);
163 | stage.setWidth(210);
164 | stage.setResizable(false);
165 | buttonToggleEdit.setText("Open edit window");
166 |
167 | } else {
168 | tabPane.setVisible(true);
169 | stage.setResizable(true);
170 | stage.setWidth(1200);
171 |
172 | buttonToggleEdit.setText("Close edit window");
173 | }
174 |
175 | if (actionEvent != null) {
176 | Preferences.getInstance().setEditWindowIsOpen(tabPane.isVisible());
177 | }
178 | } else {
179 | Logger.e("handleToggleMainView WTF: " + stage + " " + tabPane);
180 | }
181 | }
182 |
183 | public void handleAlwaysOnTop(ActionEvent actionEvent) {
184 | if (stage != null && checkBoxAlwaysOnTop != null) {
185 | stage.setAlwaysOnTop(checkBoxAlwaysOnTop.isSelected());
186 |
187 | if (actionEvent != null) {
188 | Preferences.getInstance().setWindowIsAlwaysOn(checkBoxAlwaysOnTop.isSelected());
189 | }
190 | } else {
191 | Logger.e("handleAlwaysOnTop WTF: " + stage + " " + checkBoxAlwaysOnTop);
192 | }
193 |
194 | }
195 |
196 | public void onOpenAppDirectory(ActionEvent actionEvent) {
197 | if (Desktop.isDesktopSupported()) {
198 | try {
199 | Desktop.getDesktop().open(Preferences.getInstance().getAppFolder());
200 | } catch (IOException e) {
201 | e.printStackTrace();
202 | }
203 | }
204 | }
205 |
206 | public void setStageAndSetupListeners(Stage stage) {
207 | this.stage = stage;
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/src/application/FileUtils.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import application.log.Logger;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 | import java.nio.charset.Charset;
8 | import java.nio.file.Files;
9 | import java.nio.file.OpenOption;
10 | import java.nio.file.Paths;
11 | import java.nio.file.StandardOpenOption;
12 | import java.util.ArrayList;
13 |
14 | public class FileUtils {
15 | public static String readFile(String path) throws IOException {
16 | byte[] encoded = Files.readAllBytes(Paths.get(path));
17 | return new String(encoded, Charset.defaultCharset());
18 | }
19 |
20 | public static void writeToFile(String path, String jsonCommandBatch) throws IOException {
21 |
22 | if (new File(path).exists()){
23 | new File(path).delete();
24 | }
25 |
26 | Files.write(Paths.get(path), jsonCommandBatch.getBytes(), StandardOpenOption.CREATE);
27 | }
28 |
29 | public static void getFilesInFolderRecursivly(String directoryName, ArrayList files) {
30 | File directory = new File(directoryName);
31 |
32 | // get all the files from a directory
33 | File[] fList = directory.listFiles();
34 | for (File file : fList) {
35 | if (file.isFile()) {
36 | files.add(file);
37 | } else if (file.isDirectory()) {
38 | getFilesInFolderRecursivly(file.getAbsolutePath(), files);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/application/FolderUtil.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import application.preferences.Preferences;
4 |
5 | import java.io.File;
6 |
7 | public class FolderUtil {
8 |
9 | public static File getApkFolder() {
10 |
11 | File apkFolder = new File(Preferences.getInstance().getPrimaryAPKFolder());
12 | if (!apkFolder.exists()){
13 | apkFolder.mkdir();
14 | }
15 |
16 | return apkFolder;
17 | }
18 |
19 | public static File getSnapshotFolder() {
20 |
21 | File apkFolder = new File(Preferences.getInstance().getSnapshotFolder());
22 | if (!apkFolder.exists()){
23 | apkFolder.mkdir();
24 | }
25 |
26 | return apkFolder;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/application/Main.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | import application.log.Logger;
4 | import application.preferences.Preferences;
5 | import javafx.application.Application;
6 | import javafx.application.HostServices;
7 | import javafx.application.Platform;
8 | import javafx.event.EventHandler;
9 | import javafx.fxml.FXMLLoader;
10 | import javafx.scene.image.Image;
11 | import javafx.stage.Stage;
12 | import javafx.stage.WindowEvent;
13 | import application.services.DeviceMonitorService;
14 | import javafx.scene.Parent;
15 | import javafx.scene.Scene;
16 |
17 | import javax.swing.*;
18 | import java.io.File;
19 | import java.io.PrintStream;
20 | import java.net.URL;
21 |
22 | public class Main extends Application {
23 |
24 | public static HostServices hostService;
25 |
26 | @Override
27 | public void start(Stage primaryStage) throws Exception {
28 |
29 | DeviceMonitorService.instance.startMonitoringDevices();
30 |
31 | if (Preferences.getInstance().isDebug()) {
32 | System.setOut(new PrintStream(Preferences.getInstance().getLogFile()));
33 | System.setErr(new PrintStream(Preferences.getInstance().getLogFileErr()));
34 | }
35 |
36 | if (Preferences.getInstance().isFirstRun()) {
37 |
38 | findADBPath();
39 | }
40 |
41 | hostService = getHostServices();
42 |
43 | FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLMain.fxml"));
44 |
45 | Parent root = loader.load();
46 | Scene scene = new Scene(root, 1200, 620);
47 |
48 | Image iconImage = new Image("/res/icon.png");
49 | primaryStage.getIcons().add(iconImage);
50 |
51 | try {
52 | /* URL iconURL = Main.class.getResource("/res/icon.png");
53 | java.awt.Image image = new ImageIcon(iconURL).getImage();
54 | com.apple.eawt.Application.getApplication().setDockIconImage(image);*/
55 | } catch (Exception e) {
56 | // Won't work on Windows or Linux.
57 | }
58 |
59 |
60 | primaryStage.setTitle("ADB GUI Tool");
61 | primaryStage.setScene(scene);
62 |
63 | FXMLMainController controller = (FXMLMainController)loader.getController();
64 | controller.setStageAndSetupListeners(primaryStage); // or what you want to do
65 |
66 | //primaryStage.setResizable(false);
67 | primaryStage.show();
68 |
69 | primaryStage.setOnCloseRequest(new EventHandler() {
70 | @Override
71 | public void handle(WindowEvent e) {
72 | Platform.exit();
73 | System.exit(0);
74 | }
75 | });
76 |
77 | if (FolderUtil.getSnapshotFolder().getAbsolutePath().contains(" ")){
78 | DialogUtil.showErrorDialog("This app do not support operating from a path with spaces,\n" +
79 | "please move the app and start again");
80 | System.exit(0);
81 | }
82 | }
83 |
84 | private void findADBPath() {
85 | Logger.d("Find adb on: " + Preferences.OS);
86 |
87 | if (Preferences.OS.startsWith("windows")){
88 |
89 | } else {
90 | File baseDirectory = new File("/Users/");
91 |
92 | File[] userFolder = baseDirectory.listFiles();
93 |
94 | if (userFolder != null)
95 | for (File file : userFolder) {
96 | File pathCheck = new File(file, "Library/Android/sdk/platform-tools/");
97 | if (pathCheck.exists()){
98 | Logger.d("Found adb location: " + pathCheck.getAbsolutePath());
99 | Preferences.getInstance().setAdbPath(pathCheck.getAbsolutePath() + "/");
100 | break;
101 | }
102 | }
103 | }
104 | }
105 |
106 | public static void main(String[] args) {
107 | launch(args);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/application/ProcessUtils.java:
--------------------------------------------------------------------------------
1 | package application;
2 |
3 | public class ProcessUtils {
4 |
5 | public static String[] getEnviormentParams() {
6 | String[] envp = new String [System.getenv().size()];
7 |
8 | int i = 0;
9 | for (String s : System.getenv().keySet()){
10 | //System.out.println(s + " " + System.getenv().get(s));
11 | envp[i] = s + "=" + System.getenv().get(s);
12 | i++;
13 | }
14 |
15 | return envp;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/application/apks/APKsTab.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/application/apks/APKsTabController.java:
--------------------------------------------------------------------------------
1 | package application.apks;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URL;
6 | import java.util.ArrayList;
7 | import java.util.ResourceBundle;
8 | import java.util.Set;
9 |
10 | import application.*;
11 | import application.log.Logger;
12 | import application.preferences.Preferences;
13 | import javafx.beans.value.ChangeListener;
14 | import javafx.beans.value.ObservableValue;
15 | import javafx.collections.FXCollections;
16 | import javafx.collections.ObservableList;
17 | import javafx.event.ActionEvent;
18 | import javafx.fxml.FXML;
19 | import javafx.fxml.Initializable;
20 | import javafx.scene.control.CheckBox;
21 | import javafx.scene.control.ListView;
22 | import javafx.scene.control.TextField;
23 |
24 | public class APKsTabController implements Initializable {
25 |
26 | @FXML
27 | public TextField textFieldFilter;
28 | public CheckBox checkBoxOpenAfterInstall;
29 |
30 | ObservableList apksListItems = FXCollections.observableArrayList();
31 | ArrayList apksPathes = new ArrayList<>();
32 |
33 | @FXML
34 | private ListView listAPKs;
35 |
36 | @Override
37 | public void initialize(URL location, ResourceBundle resources) {
38 | listAPKs.setItems(apksListItems);
39 |
40 | textFieldFilter.textProperty().addListener(new ChangeListener() {
41 |
42 | @Override
43 | public void changed(ObservableValue extends String> observable, String oldValue, String newValue) {
44 | filter();
45 | }
46 | });
47 |
48 | }
49 |
50 | @FXML
51 | private void handleInstallAction(ActionEvent event) {
52 | String selectedApk = listAPKs.getSelectionModel().getSelectedItem();
53 | if (selectedApk != null) {
54 | AdbUtils.executor.execute(new Runnable() {
55 | @Override
56 | public void run() {
57 | Set packages = null;
58 | if (checkBoxOpenAfterInstall.isSelected()) {
59 | packages = ADBHelper.getPackages();
60 | }
61 |
62 | Logger.ds("Trying to install: " + selectedApk);
63 | String result = ADBHelper.install(selectedApk);
64 |
65 | if (result != null) {
66 | Logger.es(result);
67 | } else {
68 | Logger.fs("Installed: " + selectedApk);
69 |
70 |
71 | if (packages != null) {
72 | boolean found = false;
73 | Set newPackages = ADBHelper.getPackages();
74 |
75 | for (String packageNew : newPackages) {
76 | if (!packages.contains(packageNew)) {
77 | found = true;
78 | Logger.ds("Found new pakcage: " + packageNew);
79 | ADBHelper.openApp(packageNew);
80 | Logger.ds("Opened package: " + packageNew);
81 | break;
82 | }
83 | }
84 |
85 | if (!found) {
86 | Logger.es("Didnt found new package to open");
87 | }
88 | }
89 | }
90 | }
91 | });
92 | }
93 | }
94 |
95 | @FXML
96 | private void handleObfuscationToolAction(ActionEvent event) {
97 |
98 | if (Preferences.getInstance().getObfuscationToolPath() == null ||
99 | Preferences.getInstance().getObfuscationToolPath().trim().equals("")) {
100 | DialogUtil.showErrorDialog("Go to preference tab and Download / Configure Obfuscation tool");
101 | return;
102 | }
103 |
104 | String selectedApk = listAPKs.getSelectionModel().getSelectedItem();
105 | if (selectedApk != null) {
106 | if (Preferences.getInstance().getObfuscationToolPath() != null) {
107 | new Thread(new Runnable() {
108 |
109 | @Override
110 | public void run() {
111 |
112 | try {
113 |
114 | Process process = Runtime.getRuntime().exec(
115 | Preferences.getInstance().getObfuscationToolPath() + " " + selectedApk, ProcessUtils.getEnviormentParams());
116 | /*BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
117 |
118 | String line;
119 | while ((line = reader.readLine()) != null) {
120 | System.out.println(line);
121 | }*/
122 | } catch (IOException e) {
123 | e.printStackTrace();
124 | }
125 | }
126 | }).start();
127 | }
128 | } else {
129 | DialogUtil.showErrorDialog("Please select APK to analyze");
130 | }
131 | }
132 |
133 | public void refreshList() {
134 | apksPathes.clear();
135 |
136 | String[] folders = Preferences.getInstance().getAPKsFolders();
137 |
138 | for (String folderPath : folders) {
139 |
140 | File folder = new File(folderPath);
141 |
142 | if (folder != null && folder.isDirectory()) {
143 | ArrayList files = new ArrayList();
144 | FileUtils.getFilesInFolderRecursivly(folder.getAbsolutePath(), files);
145 | for (File file : files) {
146 | if (file.getName().endsWith(".apk")) {
147 | apksPathes.add(file.getAbsolutePath());
148 | }
149 | }
150 | }
151 | }
152 |
153 | filter();
154 | }
155 |
156 | private void filter() {
157 | apksListItems.clear();
158 |
159 | for (String path : apksPathes) {
160 | if (isInFilter(path)) {
161 | apksListItems.add(path);
162 | }
163 | }
164 | }
165 |
166 | private boolean isInFilter(String path) {
167 | return path.contains(textFieldFilter.getText());
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/application/application.css:
--------------------------------------------------------------------------------
1 | /* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
--------------------------------------------------------------------------------
/src/application/applications/ApplicationsTab.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/application/applications/ApplicationsTabController.java:
--------------------------------------------------------------------------------
1 | package application.applications;
2 |
3 | import java.io.File;
4 | import java.net.URL;
5 | import java.util.*;
6 |
7 | import application.ADBHelper;
8 | import application.AdbUtils;
9 | import application.DialogUtil;
10 | import application.FolderUtil;
11 | import javafx.beans.value.ChangeListener;
12 | import javafx.beans.value.ObservableValue;
13 | import javafx.collections.FXCollections;
14 | import javafx.collections.ObservableList;
15 | import javafx.event.ActionEvent;
16 | import javafx.fxml.FXML;
17 | import javafx.fxml.Initializable;
18 | import javafx.scene.control.CheckBox;
19 | import javafx.scene.control.ListView;
20 | import javafx.scene.control.TextField;
21 | import application.log.Logger;
22 | import application.model.Application;
23 | import application.model.Device;
24 | import application.model.Model;
25 | import application.model.ModelListener;
26 | import application.model.PackageProcess;
27 | import application.services.ApplicationsMonitorService;
28 | import javafx.scene.control.TextInputDialog;
29 |
30 | public class ApplicationsTabController implements Initializable {
31 |
32 | private Device device;
33 |
34 | ObservableList applicationsListItems = FXCollections.observableArrayList();
35 |
36 | @FXML
37 | private ListView listApplications;
38 |
39 | @FXML
40 | private TextField textFieldFilter;
41 |
42 | @FXML
43 | private CheckBox checkBoxOnlyRunnig;
44 |
45 | ArrayList applications = new ArrayList();
46 |
47 | @Override
48 | public void initialize(URL location, ResourceBundle resources) {
49 | listApplications.setItems(applicationsListItems);
50 |
51 | textFieldFilter.textProperty().addListener(new ChangeListener() {
52 |
53 | @Override
54 | public void changed(ObservableValue extends String> observable, String oldValue, String newValue) {
55 | filter();
56 | }
57 | });
58 |
59 | checkBoxOnlyRunnig.selectedProperty().addListener(new ChangeListener() {
60 |
61 | @Override
62 | public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
63 | filter();
64 | }
65 | });
66 | }
67 |
68 | private void loadApps() {
69 | Logger.d("load apps");
70 | Application previousSelectedApp = getSelectedApplication();
71 | applicationsListItems.clear();
72 |
73 | applications.clear();
74 |
75 | if (device != null){
76 | applications.addAll(device.getApplications());
77 |
78 | Collections.sort(applications, new Comparator() {
79 |
80 | @Override
81 | public int compare(Application o1, Application o2) {
82 | return o1.getApplicationName().compareTo(o2.getApplicationName());
83 | }
84 | });
85 | }
86 |
87 | filter();
88 |
89 | selectApplication(previousSelectedApp);
90 | }
91 |
92 | private void selectApplication(Application previousSelectedApp) {
93 | if (previousSelectedApp != null){
94 | int i = 0;
95 | for (String applicationString : applicationsListItems){
96 | if (applicationString.contains(previousSelectedApp.getApplicationName())){
97 | listApplications.getSelectionModel().select(i);
98 | break;
99 | }
100 | i++;
101 | }
102 | }
103 | }
104 |
105 | private void filter() {
106 | applicationsListItems.clear();
107 |
108 | for (Application application: applications){
109 | if (isInFilter(application)){
110 | applicationsListItems.add(application.getApplicationName() +
111 | (application.isRunning() ? " RUNNING " + application.getPackageProcesses().size() : ""));
112 | }
113 | }
114 | }
115 |
116 | private boolean isInFilter(Application application) {
117 | return application.getApplicationName().contains(textFieldFilter.getText()) &&
118 | (!checkBoxOnlyRunnig.selectedProperty().get() || application.isRunning());
119 | }
120 |
121 | @FXML
122 | private void handleOpenClicked(ActionEvent event) {
123 | if (isAppSelected()){
124 | AdbUtils.executor.execute(new Runnable() {
125 | @Override
126 | public void run() {
127 | Logger.ds("Open: " + getSelectedApplication().getApplicationName());
128 | String result = ADBHelper.openApp(getGetSelectedAppPackage());
129 | if (result == null){
130 | Logger.fs("Opened: " + getSelectedApplication().getApplicationName());
131 | } else {
132 | Logger.es("Error opening: " + getSelectedApplication().getApplicationName() + " " + result);
133 | }
134 | }
135 | });
136 | }
137 | }
138 |
139 | @FXML
140 | private void uninstallClicked(ActionEvent event) {
141 | if (isAppSelected()){
142 | AdbUtils.executor.execute(new Runnable() {
143 | @Override
144 | public void run() {
145 | String selectedPackage = getGetSelectedAppPackage();
146 | Logger.ds("Uninstaling: " + selectedPackage);
147 | String result = AdbUtils.run("uninstall " + selectedPackage);
148 |
149 | if (result.trim().equals("Success")){
150 | Logger.fs("Uninstalled: " + selectedPackage);
151 | } else {
152 | Logger.es("Error Uninstaling: " + selectedPackage + " " + result);
153 | }
154 | }
155 | });
156 | }
157 | }
158 |
159 | @FXML
160 | private void handleClearDataClicked(ActionEvent event) {
161 |
162 | if (isAppSelected()){
163 |
164 | AdbUtils.executor.execute(new Runnable() {
165 | @Override
166 | public void run() {
167 | String result = ADBHelper.clearData(getGetSelectedAppPackage());
168 | if (result == null){
169 | Logger.fs("Cleared data: " + getGetSelectedAppPackage());
170 | } else {
171 | Logger.es("Cleared data failed: " + result);
172 | }
173 | }
174 | });
175 | }
176 | }
177 |
178 | public String getGetSelectedAppPackage(){
179 | return getSelectedApplication().getPackageName();
180 | }
181 |
182 | @FXML
183 | private void handleKillClicked(ActionEvent event) {
184 |
185 | if (isAppSelected()){
186 | Application application = getSelectedApplication();
187 |
188 | if (application.getPackageProcesses() == null){
189 | DialogUtil.showErrorDialog("Package should have running processes");
190 | return;
191 | }
192 |
193 | for (PackageProcess packageProcess: application.getPackageProcesses()){
194 |
195 | AdbUtils.executor.execute(new Runnable() {
196 | @Override
197 | public void run() {
198 | String result = ADBHelper.kill(application.getPackageName(), packageProcess.PID);
199 | if (result == null){
200 | Logger.fs("Killed: " + application.getPackageName());
201 | } else {
202 | Logger.es(result);
203 | }
204 | }
205 | });
206 |
207 | }
208 | }
209 | }
210 |
211 | @FXML
212 | private void handleGetAPKClicked(ActionEvent event) {
213 |
214 | if (isAppSelected()){
215 | final Application application = getSelectedApplication();
216 | AdbUtils.executor.execute(new Runnable() {
217 |
218 | @Override
219 | public void run() {
220 | Logger.ds("Getting apk path for: " + application.getApplicationName());
221 | String path = AdbUtils.run("shell pm path " + application.getPackageName());
222 |
223 | if (!path.startsWith("package:")){
224 | Logger.es("Error getting path for apk: " + application.getApplicationName() + ": " + path);
225 | return;
226 | }
227 |
228 | File apkFolder = FolderUtil.getApkFolder();
229 |
230 | String tempApkFile = "/sdcard/base.apk";
231 | String from = path.replace("package:", "");
232 |
233 | Logger.ds("Copy: " + from + " to: " + tempApkFile);
234 |
235 | String result = AdbUtils.run("shell cp " + from + " " + tempApkFile);
236 | if (!result.trim().equals("")){
237 | Logger.es("Error copying: " + from + " to: " + tempApkFile);
238 | return;
239 | }
240 |
241 | File apkToCreate = new File(apkFolder.getAbsolutePath(), application.getPackageName() + ".apk");
242 | Logger.ds("pulling: " + from + " to: " + apkToCreate.getAbsolutePath());
243 |
244 | if (ADBHelper.pull(tempApkFile, apkToCreate.getAbsolutePath())){
245 | Logger.fs("File pulled to: " + apkToCreate.getAbsolutePath());
246 | } else {
247 | Logger.es("Error pulling: " + result);
248 | }
249 |
250 | result = ADBHelper.rm(tempApkFile);
251 | }
252 | });
253 | }
254 | }
255 |
256 | private Application getSelectedApplication() {
257 |
258 | if (listApplications != null) {
259 | for (Application application : applications) {
260 | if (application != null && application.getPackageName() != null &&
261 | listApplications.getSelectionModel().getSelectedItem() != null &&
262 | application.getPackageName().equals(listApplications.getSelectionModel().getSelectedItem().split(" ")[0])) {
263 | return application;
264 | }
265 | }
266 | }
267 | return null;
268 | }
269 |
270 | private boolean isAppSelected() {
271 |
272 | if (listApplications.getSelectionModel().getSelectedIndex() > -1){
273 | return true;
274 | } else {
275 | DialogUtil.showErrorDialog("Please select package first");
276 | return false;
277 | }
278 | }
279 |
280 | public void stopWorking() {
281 | Logger.d("stop analyzing apps");
282 | ApplicationsMonitorService.instance.stop();
283 | Model.instance.removeModelListener(deviceChangeListener);
284 |
285 | if (device != null){
286 | device.removeModelListener(deviceApplicationsListener);
287 | }
288 | }
289 |
290 | public void startWorking() {
291 | Logger.d("start analyzing apps");
292 | ApplicationsMonitorService.instance.start();
293 |
294 | Model.instance.addSelectedDeviceListener(deviceChangeListener);
295 | setDeviceData();
296 | }
297 |
298 | private ModelListener deviceChangeListener = new ModelListener() {
299 |
300 | @Override
301 | public void onChangeModelListener() {
302 | Logger.d("Application Controler: Detected device change");
303 |
304 | setDeviceData();
305 | }
306 | };
307 |
308 | private ModelListener deviceApplicationsListener = new ModelListener() {
309 |
310 | @Override
311 | public void onChangeModelListener() {
312 | Logger.d("Application Controler: Detected applications change");
313 | loadApps();
314 |
315 | }
316 | };
317 |
318 | protected void setDeviceData() {
319 |
320 | if (device != null){
321 | device.removeModelListener(deviceApplicationsListener);
322 | }
323 |
324 | device = Model.instance.getSelectedDevice();
325 | if (device != null){
326 | device.addModelListener(deviceApplicationsListener);
327 | loadApps();
328 | }
329 | }
330 |
331 | public void runMonkeyClicked(ActionEvent actionEvent) {
332 |
333 | if (isAppSelected()) {
334 | final Application application = getSelectedApplication();
335 |
336 | TextInputDialog dialog = new TextInputDialog("500");
337 | dialog.setTitle("Monkey runner");
338 | dialog.setHeaderText("Enter amount of steps to run:");
339 | //dialog.setContentText("Enter amount of steps to run:");
340 |
341 | Optional result = dialog.showAndWait();
342 | if (result.isPresent()) {
343 | try {
344 | final int numberOfSteps = Integer.parseInt(result.get());
345 |
346 | AdbUtils.executor.execute(new Runnable() {
347 | @Override
348 | public void run() {
349 |
350 | Logger.ds("Running monkey of: " + application.getApplicationName() + " steps: " + numberOfSteps);
351 |
352 | String result = ADBHelper.runMonkey(application.getApplicationName(), numberOfSteps, 100);
353 |
354 | if (result == null){
355 | Logger.fs("Monkey Finished: " + application.getApplicationName());
356 | } else {
357 | Logger.es("Monkey failed: " + application.getApplicationName() + " " + result);
358 | }
359 | }
360 | });
361 | } catch (Exception e) {
362 | DialogUtil.showErrorDialog("Please select a number");
363 | runMonkeyClicked(null);
364 | }
365 | }
366 | }
367 | }
368 | }
369 |
--------------------------------------------------------------------------------
/src/application/batchcommands/BatchCommandEditLayout.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/application/batchcommands/BatchCommandTab.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/application/batchcommands/BatchCommandTabController.java:
--------------------------------------------------------------------------------
1 | package application.batchcommands;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.net.URL;
6 | import java.util.ArrayList;
7 | import java.util.ResourceBundle;
8 |
9 | import application.AdbUtils;
10 | import application.DialogUtil;
11 | import application.FileUtils;
12 | import application.model.Model;
13 | import application.preferences.Preferences;
14 | import com.google.gson.Gson;
15 |
16 | import javafx.application.Platform;
17 | import javafx.beans.value.ChangeListener;
18 | import javafx.beans.value.ObservableValue;
19 | import javafx.collections.FXCollections;
20 | import javafx.collections.ObservableList;
21 | import javafx.event.ActionEvent;
22 | import javafx.fxml.FXML;
23 | import javafx.fxml.Initializable;
24 | import javafx.scene.control.ListView;
25 | import javafx.scene.control.TextArea;
26 | import application.log.Logger;
27 | import application.model.Command;
28 | import application.model.CommandBatch;
29 |
30 | public class BatchCommandTabController implements Initializable {
31 |
32 | ObservableList batchesListItems = FXCollections.observableArrayList();
33 |
34 | ArrayList commandBatches = Model.instance.getCommandBatches();
35 |
36 | @FXML
37 | private ListView listBatches;
38 |
39 | @FXML
40 | private BatchCommandsListView listCommands;
41 |
42 | @FXML
43 | private TextArea textAreaLog;
44 | private int runningIndex = -1;
45 |
46 | @Override
47 | public void initialize(URL location, ResourceBundle resources) {
48 | listBatches.setItems(batchesListItems);
49 |
50 | loadBatches();
51 |
52 | listBatches.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
53 | public void changed(ObservableValue extends String> ov, String old_val, String new_val) {
54 | updateCommandsList();
55 | }
56 | });
57 | }
58 |
59 | protected void updateCommandsList() {
60 |
61 | int index = listBatches.getSelectionModel().getSelectedIndex();
62 |
63 | if (index > -1) {
64 | CommandBatch commandBatch = commandBatches.get(index);
65 | listCommands.update(commandBatch.commands, runningIndex);
66 | }
67 | }
68 |
69 | @FXML
70 | private void handleRunBatchClicked(ActionEvent event) {
71 |
72 | int index = listBatches.getSelectionModel().getSelectedIndex();
73 |
74 | if (index > -1) {
75 | CommandBatch commandBatch = commandBatches.get(index);
76 | runCommands(commandBatch, 0, false);
77 | } else {
78 | DialogUtil.showErrorDialog("Please select command batch");
79 | }
80 | }
81 |
82 | @FXML
83 | private void handleRunSingleCommandClicked(ActionEvent event) {
84 | int index = listBatches.getSelectionModel().getSelectedIndex();
85 |
86 | if (index > -1) {
87 | int commandIndex = listCommands.getSelectionModel().getSelectedIndex();
88 | if (commandIndex > -1){
89 | CommandBatch commandBatch = commandBatches.get(index);
90 | runCommands(commandBatch, commandIndex, true);
91 | }
92 | }
93 | }
94 |
95 | @FXML
96 | private void handleRunForwardSingleCommandClicked(ActionEvent event) {
97 | int index = listBatches.getSelectionModel().getSelectedIndex();
98 |
99 | if (index > -1) {
100 | int commandIndex = listCommands.getSelectionModel().getSelectedIndex();
101 | if (commandIndex > -1){
102 | CommandBatch commandBatch = commandBatches.get(index);
103 | runCommands(commandBatch, commandIndex, false);
104 | }
105 | } else {
106 | DialogUtil.showErrorDialog("Please select command to run from");
107 | }
108 | }
109 |
110 | public void runCommands(CommandBatch commandBatch, int runFromIndex, boolean singleCommand) {
111 | AdbUtils.executor.execute(new Runnable() {
112 |
113 | @Override
114 | public void run() {
115 | int toIndex = (singleCommand ? runFromIndex + 1 : commandBatch.commands.size());
116 | for (int i = runFromIndex; i < toIndex; i++) {
117 | updateRunningIndex(i);
118 | executeCommand(commandBatch, i);
119 | Logger.fs("Finished batch commands: " + commandBatch.name);
120 | }
121 |
122 | updateRunningIndex(-1);
123 | log("Finished executing commands");
124 | }
125 | });
126 | }
127 |
128 | protected void updateRunningIndex(int i) {
129 | runningIndex = i;
130 | Platform.runLater(new Runnable() {
131 | @Override
132 | public void run() {
133 | updateCommandsList();
134 | }
135 | });
136 | }
137 |
138 | protected void executeCommand(CommandBatch commandBatch, int i) {
139 | Logger.ds("Running command: " + commandBatch.name + " " + commandBatch.commands.get(i).description);
140 |
141 | updateRunningIndex(i);
142 | Command command = commandBatch.commands.get(i);
143 | log("Run: " + command.command);
144 | log(AdbUtils.run(command));
145 | updateRunningIndex(-1);
146 | }
147 |
148 |
149 | @FXML
150 | private void handleCreateNewClicked(ActionEvent event) {
151 | try {
152 | BatchCommandEditController.showScreen(getClass(), null, null, batchCommandEditControllerListener);
153 | } catch (IOException e) {
154 | e.printStackTrace();
155 | }
156 | }
157 |
158 | @FXML
159 | private void handleEditNewClicked(ActionEvent event) {
160 | int index = listBatches.getSelectionModel().getSelectedIndex();
161 |
162 | if (index > -1) {
163 | try {
164 | CommandBatch commandBatch = commandBatches.get(index);
165 |
166 | BatchCommandEditController.showScreen(getClass(), commandBatch, listBatches.getSelectionModel().getSelectedItem(), batchCommandEditControllerListener);
167 | } catch (IOException e) {
168 | e.printStackTrace();
169 | }
170 | } else {
171 | log("select batch to edit");
172 | }
173 | }
174 |
175 | @FXML
176 | private void handleCopyClicked(ActionEvent event) {
177 | int index = listBatches.getSelectionModel().getSelectedIndex();
178 |
179 | if (index > -1) {
180 | try {
181 | CommandBatch commandBatch = commandBatches.get(index);
182 |
183 | BatchCommandEditController.showScreen(getClass(), commandBatch, null, batchCommandEditControllerListener);
184 | } catch (IOException e) {
185 | e.printStackTrace();
186 | }
187 | } else {
188 | log("select batch to copy");
189 | }
190 | }
191 |
192 | @FXML
193 | private void handleDeleteClicked(ActionEvent event) {
194 | int index = listBatches.getSelectionModel().getSelectedIndex();
195 |
196 | if (index > -1) {
197 | CommandBatch commandBatch = commandBatches.get(index);
198 | new File(Preferences.getInstance().getCommandFolder(), listBatches.getSelectionModel().getSelectedItem()).delete();
199 | loadBatches();
200 |
201 |
202 | } else {
203 | log("select batch to delete");
204 | }
205 | }
206 |
207 | private void log(String text) {
208 | textAreaLog.appendText("\n" + text);
209 | }
210 |
211 | @FXML
212 | private void handleRefreshClicked(ActionEvent event) {
213 | loadBatches();
214 | }
215 |
216 | private BatchCommandEditController.BatchCommandEditControllerListener batchCommandEditControllerListener = new BatchCommandEditController.BatchCommandEditControllerListener() {
217 | @Override
218 | public void onBatchCommandUpdated() {
219 | loadBatches();
220 | }
221 | };
222 |
223 | private void loadBatches() {
224 |
225 | Logger.d("Working Directory = " + System.getProperty("user.dir"));
226 | batchesListItems.clear();
227 | listCommands.clear();
228 |
229 | Model.instance.loadCommandBatches();
230 |
231 | for (CommandBatch commandBatch : commandBatches) {
232 | batchesListItems.add(commandBatch.name);
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/application/batchcommands/BatchCommandViewUtil.java:
--------------------------------------------------------------------------------
1 | package application.batchcommands;
2 |
3 | import application.model.Command;
4 |
5 | public class BatchCommandViewUtil {
6 |
7 | public static String getCommandUIString(Command command) {
8 | return command.description + " (" + command.type + "): " + command.command;
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/application/batchcommands/BatchCommandsListView.java:
--------------------------------------------------------------------------------
1 | package application.batchcommands;
2 |
3 | import javafx.collections.FXCollections;
4 | import javafx.collections.ObservableList;
5 | import javafx.scene.control.ListView;
6 | import application.model.Command;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * Created by evgeni.shafran on 10/10/16.
12 | */
13 | public class BatchCommandsListView extends ListView {
14 |
15 | ObservableList commandsListItems = FXCollections.observableArrayList();
16 |
17 |
18 | public void update(List commands, int runningIndex) {
19 | setItems(commandsListItems);
20 | commandsListItems.clear();
21 | int i = 0;
22 |
23 | for (Command command : commands) {
24 | String runningString = "";
25 | if (i == runningIndex) {
26 | runningString = "RUNNING ";
27 | }
28 |
29 | commandsListItems.add(runningString + BatchCommandViewUtil.getCommandUIString(command));
30 |
31 | i++;
32 | }
33 | }
34 |
35 | public void clear() {
36 | commandsListItems.clear();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/application/batchcommands/CommandWizardController.java:
--------------------------------------------------------------------------------
1 | package application.batchcommands;
2 |
3 | import javafx.event.ActionEvent;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.fxml.Initializable;
6 | import javafx.scene.Parent;
7 | import javafx.scene.Scene;
8 | import javafx.scene.control.RadioButton;
9 | import javafx.scene.control.TextField;
10 | import javafx.stage.Modality;
11 | import javafx.stage.Stage;
12 |
13 | import java.io.IOException;
14 | import java.net.URL;
15 | import java.util.ResourceBundle;
16 |
17 | /**
18 | * Created by evgeni.shafran on 11/13/16.
19 | */
20 | public class CommandWizardController implements Initializable {
21 | public RadioButton radioButtonInputText;
22 | public RadioButton radioButtonInputTab;
23 | public RadioButton radioButtonInputEnter;
24 | public TextField textFieldInputText;
25 | public RadioButton radioButtonInputPower;
26 | public RadioButton radioButtonInputVolumeUp;
27 | public RadioButton radioButtonInputVolumeDown;
28 | public RadioButton radioButtonInputRecent;
29 | public RadioButton radioButtonInputHome;
30 | public RadioButton radioButtonInputBack;
31 | public RadioButton radioButtonFilesPush;
32 | public RadioButton radioButtonFilesPull;
33 | public RadioButton radioButtonFilesInstallApk;
34 | public TextField textFieldFilesPushFrom;
35 | public TextField textFieldFilesPushTo;
36 | public TextField textFieldFilesPullFrom;
37 | public TextField textFieldFilesPullTo;
38 | public TextField textFieldFilesApkPath;
39 | public RadioButton radioButtonFilesUnInstallApk;
40 | public TextField textFieldFilesUninstallApp;
41 | public RadioButton radioButtonFilesClearData;
42 | public TextField textFieldFilesClearData;
43 | public RadioButton radioButtonFilesOpenApp;
44 | public TextField textFieldFilesOpenApp;
45 | private CommandWizardControllerListener commandWizardControllerListener;
46 |
47 | public interface CommandWizardControllerListener {
48 | public void onCommandSelected(String command, String description);
49 | }
50 |
51 | @Override
52 | public void initialize(URL location, ResourceBundle resources) {
53 |
54 | }
55 |
56 | public static void showScreen(Class class1, CommandWizardControllerListener commandWizardControllerListener) throws IOException {
57 | FXMLLoader fxmlLoader = new FXMLLoader(class1.getResource("/application/batchcommands/CommandWizardLayout.fxml"));
58 |
59 | Parent root1 = (Parent) fxmlLoader.load();
60 |
61 | CommandWizardController controller = fxmlLoader.getController();
62 | controller.setCommandWizardControllerListener(commandWizardControllerListener);
63 |
64 | Stage stage = new Stage();
65 | stage.initModality(Modality.APPLICATION_MODAL);
66 | stage.setTitle("Commands Wizard");
67 | stage.setScene(new Scene(root1));
68 | stage.show();
69 | }
70 |
71 | private void setCommandWizardControllerListener(CommandWizardControllerListener commandWizardControllerListener) {
72 | this.commandWizardControllerListener = commandWizardControllerListener;
73 | }
74 |
75 | public void addCommandClicked(ActionEvent actionEvent) {
76 | String command = "";
77 | String description = "";
78 |
79 | if (radioButtonInputText.isSelected()){
80 | command = "shell input text '" + textFieldInputText.getText() + "'";
81 | description = "Enter text";
82 | } else if (radioButtonInputTab.isSelected()){
83 | command = "shell input keyevent KEYCODE_TAB";
84 | description = "Next field";
85 | } else if (radioButtonInputEnter.isSelected()){
86 | command = "shell input keyevent KEYCODE_ENTER";
87 | description = "Submit";
88 | } else if (radioButtonInputPower.isSelected()){
89 | command = "shell input keyevent KEYCODE_POWER";
90 | description = "Power";
91 | } else if (radioButtonInputVolumeDown.isSelected()){
92 | command = "shell input keyevent KEYCODE_VOLUME_DOWN";
93 | description = "Volume down";
94 | } else if (radioButtonInputVolumeUp.isSelected()){
95 | command = "shell input keyevent KEYCODE_VOLUME_UP";
96 | description = "Volume up";
97 | } else if (radioButtonInputBack.isSelected()){
98 | command = "shell input keyevent KEYCODE_BACK";
99 | description = "Back";
100 | } else if (radioButtonInputHome.isSelected()){
101 | command = "shell input keyevent KEYCODE_HOME";
102 | description = "Home";
103 | } else if (radioButtonInputRecent.isSelected()){
104 | command = "shell input keyevent KEYCODE_APP_SWITCH";
105 | description = "Recent";
106 | }
107 |
108 | else if (radioButtonFilesPush.isSelected()){
109 | command = "push " + textFieldFilesPushFrom.getText() + "" + " " + textFieldFilesPushTo.getText() + "";
110 | description = "Push file to device";
111 | } else if (radioButtonFilesPull.isSelected()){
112 | command = "pull " + textFieldFilesPullFrom.getText() + "" + " " + textFieldFilesPullTo.getText() + "";
113 | description = "Pull file from device";
114 | } else if (radioButtonFilesInstallApk.isSelected()){
115 | command = "install -r " + textFieldFilesApkPath.getText() + "";
116 | description = "Install Apk";
117 | } else if (radioButtonFilesUnInstallApk.isSelected()){
118 | command = "uninstall " + textFieldFilesUninstallApp.getText();
119 | description = "Uninstall app: " + textFieldFilesUninstallApp.getText();
120 | } else if (radioButtonFilesClearData.isSelected()){
121 | command = "shell pm clear " + textFieldFilesClearData.getText();
122 | description = "Clear data: " + textFieldFilesClearData.getText();
123 | } else if (radioButtonFilesOpenApp.isSelected()){
124 | command = "shell monkey -p " + textFieldFilesOpenApp.getText() + " 1";
125 | description = "Open app: " + textFieldFilesOpenApp.getText();
126 | }
127 |
128 | commandWizardControllerListener.onCommandSelected(command, description);
129 |
130 | ((Stage) radioButtonInputText.getScene().getWindow()).close();
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/application/batchcommands/CommandWizardLayout.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
49 |
51 |
52 |
53 |
55 |
57 |
58 |
60 |
62 |
63 |
64 |
66 |
68 |
69 |
71 |
73 |
74 |
76 |
78 |
79 |
80 |
82 |
84 |
85 |
87 |
89 |
90 |
92 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
107 |
109 |
110 |
111 |
112 |
114 |
116 |
117 |
118 |
119 |
121 |
123 |
124 |
125 |
126 |
128 |
130 |
131 |
132 |
133 |
135 |
137 |
138 |
139 |
140 |
142 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/src/application/devices/DevicesController.java:
--------------------------------------------------------------------------------
1 | package application.devices;
2 |
3 | import java.awt.*;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.net.URL;
7 | import java.nio.file.Paths;
8 | import java.util.Calendar;
9 | import java.util.List;
10 | import java.util.ResourceBundle;
11 |
12 | import application.ADBHelper;
13 | import application.AdbUtils;
14 | import application.DialogUtil;
15 | import application.intentbroadcasts.IntentBroadcast;
16 | import application.model.*;
17 | import application.screencapture.ScreenCaptureController;
18 | import application.view.DateTimePickerController;
19 | import javafx.application.Platform;
20 | import javafx.beans.value.ChangeListener;
21 | import javafx.beans.value.ObservableValue;
22 | import javafx.collections.FXCollections;
23 | import javafx.collections.ObservableList;
24 | import javafx.event.ActionEvent;
25 | import javafx.fxml.FXML;
26 | import javafx.fxml.Initializable;
27 | import javafx.scene.control.Button;
28 | import javafx.scene.control.ChoiceBox;
29 | import javafx.scene.control.ListView;
30 | import application.log.Logger;
31 | import application.services.DeviceMonitorService;
32 | import javafx.scene.control.TextField;
33 |
34 | public class DevicesController implements Initializable {
35 |
36 | public TextField textFieldDeviceInput;
37 | public ChoiceBox choiceBoxBatchCommands;
38 | boolean killed = false;
39 |
40 | ObservableList commandsListItems = FXCollections.observableArrayList();
41 |
42 |
43 | @FXML
44 | private ListView listDevices;
45 |
46 | @FXML
47 | private Button buttonADBToggle;
48 |
49 | ObservableList devicesListItems = FXCollections.observableArrayList();
50 |
51 | private List availableDevices;
52 |
53 | @Override
54 | public void initialize(URL location, ResourceBundle resources) {
55 | listDevices.setItems(devicesListItems);
56 | // listDevices.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
57 |
58 | listDevices.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
59 | public void changed(ObservableValue extends String> ov, String old_val, String new_val) {
60 | if (listDevices.getSelectionModel().getSelectedIndex() >= 0) {
61 | Model.instance.setSelectedDevice(
62 | availableDevices.get(listDevices.getSelectionModel().getSelectedIndex()));
63 | }
64 | }
65 | });
66 |
67 | choiceBoxBatchCommands.setItems(commandsListItems);
68 |
69 | choiceBoxBatchCommands.getSelectionModel().selectedItemProperty().addListener(batchCommandSelect);
70 |
71 | Model.instance.addModelListener(new ModelListener() {
72 |
73 | @Override
74 | public void onChangeModelListener() {
75 | refreshDevices();
76 | }
77 | });
78 |
79 | refreshDevices();
80 |
81 | loadBatchCommands();
82 | }
83 |
84 | ChangeListener batchCommandSelect = new ChangeListener() {
85 | @Override
86 | public void changed(ObservableValue observable, Object oldValue, Object newValue) {
87 | int selectedIndex = choiceBoxBatchCommands.getSelectionModel().getSelectedIndex();
88 | if (selectedIndex != 0) {
89 | choiceBoxBatchCommands.setDisable(true);
90 |
91 | final CommandBatch commandBatch = Model.instance.getCommandBatches().get(selectedIndex - 1);
92 |
93 | AdbUtils.executor.execute(new Runnable() {
94 |
95 | @Override
96 | public void run() {
97 | for (Command command : commandBatch.commands) {
98 | Logger.ds("Run: " + command.description);
99 | AdbUtils.run(command);
100 | }
101 |
102 | Platform.runLater(new Runnable() {
103 | @Override public void run() {
104 | choiceBoxBatchCommands.setDisable(false);
105 |
106 | choiceBoxBatchCommands.getSelectionModel().select(0);
107 | }
108 | });
109 |
110 |
111 | Logger.fs("Finished: " + commandBatch.name);
112 | }
113 | });
114 |
115 | }
116 | }
117 | };
118 |
119 | private void loadBatchCommands() {
120 | commandsListItems.clear();
121 |
122 | commandsListItems.add("Select command to RUN");
123 | choiceBoxBatchCommands.getSelectionModel().select(0);
124 |
125 | for (CommandBatch commandBatch : Model.instance.getCommandBatches()) {
126 | commandsListItems.add(commandBatch.name);
127 | }
128 | }
129 |
130 | @FXML
131 | private void handleToggleADBClicked(ActionEvent event) {
132 |
133 | devicesListItems.clear();
134 |
135 | if (killed) {
136 | DeviceMonitorService.instance.startMonitoringDevices();
137 | buttonADBToggle.setText("Kill");
138 | Logger.fs("ADB server started");
139 | } else {
140 | buttonADBToggle.setText("Start monitoring");
141 | DeviceMonitorService.instance.stopMonitoringDevices();
142 | AdbUtils.executor.execute(new Runnable() {
143 |
144 | @Override
145 | public void run() {
146 | Logger.d(ADBHelper.killServer());
147 | Logger.fs("ADB server killed");
148 | }
149 | });
150 |
151 | DialogUtil.showInfoDialog("Restarting ADB service from this tool can cause device to be 'unauthorized'\n" +
152 | "In that case please open you favourite command line (terminal) and enter:\n" +
153 | "adb devices\n" +
154 | "Then press start monitoring");
155 | }
156 |
157 | killed = !killed;
158 | }
159 |
160 | @FXML
161 | private void handleTakeSnapshotClicked(ActionEvent event) {
162 | try {
163 | ScreenCaptureController.showScreen(getClass());
164 | } catch (IOException e) {
165 | e.printStackTrace();
166 | }
167 |
168 | }
169 |
170 | private void refreshDevices() {
171 |
172 | Device selectedDevice = Model.instance.getSelectedDevice();
173 |
174 | int i = 0;
175 |
176 | devicesListItems.clear();
177 | availableDevices = Model.instance.getAvailableDevices();
178 | boolean setSelected = false;
179 | for (Device device : availableDevices) {
180 | devicesListItems.add(getDeviceDescription(device));
181 |
182 | if (selectedDevice != null && device.getId().equals(selectedDevice.getId())){
183 | listDevices.getSelectionModel().select(i);
184 | setSelected = true;
185 | }
186 |
187 | i++;
188 | }
189 |
190 | if (!setSelected && devicesListItems.size() > 0) {
191 | listDevices.getSelectionModel().select(0);
192 | }
193 | }
194 |
195 | private String getDeviceDescription(Device device) {
196 | return device.getName() + " " + device.getAndroidVersion() + " " + device.getId();
197 | }
198 |
199 | public void onDeviceInputEnter(ActionEvent actionEvent) {
200 | AdbUtils.executor.execute(new Runnable() {
201 | @Override
202 | public void run() {
203 | Logger.ds("Sending input to device: " + textFieldDeviceInput.getText());
204 |
205 | ADBHelper.sendInputText(textFieldDeviceInput.getText());
206 | Logger.fs("Text sent: " + textFieldDeviceInput.getText());
207 | textFieldDeviceInput.setText("");
208 | }
209 | });
210 | }
211 |
212 | public void handleConnectToWifiClicked(ActionEvent actionEvent) {
213 | AdbUtils.executor.execute(new Runnable() {
214 | @Override
215 | public void run() {
216 |
217 | Device selectedDevice = Model.instance.getSelectedDevice();
218 |
219 | if (selectedDevice != null) {
220 | Logger.ds("Connecting device to wifi: " + getDeviceDescription(selectedDevice));
221 | Logger.fes(ADBHelper.connectDeviceToWifi(), "Device connected, you can disconnect it from the usb port");
222 |
223 | } else {
224 | DialogUtil.showErrorDialog("Please select device first");
225 | }
226 | }
227 | });
228 | }
229 |
230 | public void onChangeEmulatorDate(ActionEvent actionEvent) {
231 | Device device = Model.instance.getSelectedDevice();
232 | if (device == null) {
233 | DialogUtil.showErrorDialog("Select emulator first");
234 | } else if (!device.isEmulator()) {
235 | DialogUtil.showErrorDialog("Changing date/time works only on emulators");
236 | } else {
237 | try {
238 | DateTimePickerController.showScreen(getClass(), new DateTimePickerController.DateTimePickerListener() {
239 | @Override
240 | public void onDateSet(Calendar calendar) {
241 | Logger.ds("Set emulator time" + (calendar.getTime()));
242 | AdbUtils.executor.execute(new Runnable() {
243 | @Override
244 | public void run() {
245 | String result = ADBHelper.setDateTime(calendar);
246 | Logger.fes(result, "Date/Time set: " + calendar.getTime());
247 | }
248 | });
249 | }
250 | });
251 | } catch (IOException e) {
252 | e.printStackTrace();
253 | }
254 | }
255 | }
256 |
257 | public void onOpenDevSettings(ActionEvent actionEvent) {
258 | AdbUtils.executor.execute(new Runnable() {
259 | @Override
260 | public void run() {
261 | IntentBroadcast intent = new IntentBroadcast();
262 | intent.activityManagerCommand = IntentBroadcast.ACTIVITY_MANAGER_COMMAND_START;
263 | intent.action = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
264 | ADBHelper.sendIntent(intent);
265 | }
266 | });
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/src/application/devices/DevicesLayout.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/application/intentbroadcasts/IntentBroadcast.java:
--------------------------------------------------------------------------------
1 | package application.intentbroadcasts;
2 |
3 | /**
4 | * Created by evgeni.shafran on 11/15/16.
5 | */
6 | public class IntentBroadcast {
7 |
8 | public static final String ACTIVITY_MANAGER_COMMAND_START = "start";
9 | public static final String ACTIVITY_MANAGER_COMMAND_BROADCAST = "broadcast";
10 | public static final String ACTIVITY_MANAGER_COMMAND_START_SERVICE = "startservice";
11 |
12 | public String activityManagerCommand = "broadcast";
13 | public String action;
14 | public String promptData = "";
15 | public String data = "";
16 | public String mimeType = "";
17 | public String category = "";
18 | public String component = "";
19 |
20 | @Override
21 | public String toString() {
22 | return "IntentBroadcast{" +
23 | "activityManagerCommand='" + activityManagerCommand + '\'' +
24 | ", action='" + action + '\'' +
25 | ", promptData='" + promptData + '\'' +
26 | ", data='" + data + '\'' +
27 | ", mimeType='" + mimeType + '\'' +
28 | ", category='" + category + '\'' +
29 | ", component='" + component + '\'' +
30 | '}';
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/application/intentbroadcasts/IntentBroadcastsController.java:
--------------------------------------------------------------------------------
1 | package application.intentbroadcasts;
2 |
3 | import application.ADBHelper;
4 | import application.AdbUtils;
5 | import application.DialogUtil;
6 | import application.FileUtils;
7 | import application.log.Logger;
8 | import application.model.CommandBatch;
9 | import application.preferences.Preferences;
10 | import com.google.gson.Gson;
11 | import javafx.beans.value.ChangeListener;
12 | import javafx.beans.value.ObservableValue;
13 | import javafx.collections.FXCollections;
14 | import javafx.collections.ObservableList;
15 | import javafx.event.ActionEvent;
16 | import javafx.fxml.Initializable;
17 | import javafx.scene.control.*;
18 | import javafx.scene.control.Button;
19 | import javafx.scene.control.TextField;
20 |
21 | import java.io.File;
22 | import java.io.IOException;
23 | import java.net.URL;
24 | import java.util.ArrayList;
25 | import java.util.ResourceBundle;
26 | import java.util.concurrent.Callable;
27 | import java.util.concurrent.TimeUnit;
28 |
29 | import static com.sun.activation.registries.LogSupport.log;
30 |
31 | public class IntentBroadcastsController implements Initializable {
32 |
33 | public TextField textFieldAction;
34 | public TextField textFieldData;
35 | public RadioButton radioButtonActivityManagerCommandStart;
36 | public RadioButton radioButtonActivityManagerCommandBroadcast;
37 | public RadioButton radioButtonActivityManagerCommandStartService;
38 | public Button buttonSend;
39 | public TextField textFieldMimeType;
40 | public TextField textFieldCategory;
41 | public TextField textFieldComponent;
42 | public ListView listSaved;
43 | public TextField textFieldName;
44 |
45 | ObservableList devicesListItems = FXCollections.observableArrayList();
46 | ObservableList savedIntentsListItems = FXCollections.observableArrayList();
47 | ArrayList intentBroadcasts = new ArrayList<>();
48 | public ChoiceBox choiceBoxActions;
49 | private ArrayList intents = new ArrayList<>();
50 |
51 | @Override
52 | public void initialize(URL location, ResourceBundle resources) {
53 | fillDefaultActions();
54 | choiceBoxActions.setItems(devicesListItems);
55 |
56 | choiceBoxActions.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
57 | @Override
58 | public void changed(ObservableValue observable, Object oldValue, Object newValue) {
59 | if (choiceBoxActions.getSelectionModel().getSelectedItem() != null) {
60 | updateDefaults(intentBroadcasts.get(choiceBoxActions.getSelectionModel().getSelectedIndex()));
61 | }
62 | choiceBoxActions.getSelectionModel().select(null);
63 | }
64 | });
65 |
66 | radioButtonActivityManagerCommandBroadcast.setSelected(true);
67 |
68 | listSaved.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
69 | @Override
70 | public void changed(ObservableValue extends String> observable, String oldValue, String newValue) {
71 | loadSelectedIntoFieleds();
72 | }
73 | });
74 |
75 | listSaved.setItems(savedIntentsListItems);
76 | loadSavedIntents();
77 | }
78 |
79 | private void loadSelectedIntoFieleds() {
80 | int index = listSaved.getSelectionModel().getSelectedIndex();
81 | if (index > -1){
82 | textFieldName.setText(listSaved.getSelectionModel().getSelectedItem());
83 |
84 | updateDefaults(intents.get(index));
85 | }
86 | }
87 |
88 | private void updateDefaults(IntentBroadcast intentBroadcast) {
89 | textFieldAction.setText(intentBroadcast.action);
90 | textFieldComponent.setText(intentBroadcast.component);
91 | textFieldCategory.setText(intentBroadcast.category);
92 | textFieldData.setText(intentBroadcast.data);
93 | textFieldMimeType.setText(intentBroadcast.mimeType);
94 | //textFieldData.setPromptText(intentBroadcast.promptData);
95 |
96 | if (intentBroadcast.activityManagerCommand.equals(IntentBroadcast.ACTIVITY_MANAGER_COMMAND_START)) {
97 | radioButtonActivityManagerCommandStart.setSelected(true);
98 | } else if (intentBroadcast.activityManagerCommand.equals(IntentBroadcast.ACTIVITY_MANAGER_COMMAND_BROADCAST)) {
99 | radioButtonActivityManagerCommandBroadcast.setSelected(true);
100 | } else if (intentBroadcast.activityManagerCommand.equals(IntentBroadcast.ACTIVITY_MANAGER_COMMAND_START_SERVICE)) {
101 | radioButtonActivityManagerCommandStartService.setSelected(true);
102 | }
103 | }
104 |
105 | //TODO should probably be loaded from disk (json)
106 | private void fillDefaultActions() {
107 |
108 | IntentBroadcast intentBroadcast = new IntentBroadcast();
109 | intentBroadcast.action = "android.intent.action.VIEW";
110 | intentBroadcast.activityManagerCommand = IntentBroadcast.ACTIVITY_MANAGER_COMMAND_START;
111 | //intentBroadcast.promptData = "Can be web url like: http://somewebsite.com";
112 |
113 | addIntentBroadcast(intentBroadcast);
114 |
115 | intentBroadcast = new IntentBroadcast();
116 | intentBroadcast.action = "android.intent.action.BOOT_COMPLETED";
117 | //intentBroadcast.promptData = null;
118 | addIntentBroadcast(intentBroadcast);
119 | }
120 |
121 | private void addIntentBroadcast(IntentBroadcast intentBroadcast) {
122 | intentBroadcasts.add(intentBroadcast);
123 | devicesListItems.add(intentBroadcast.action);
124 | }
125 |
126 | public void onButtonSendClicked(ActionEvent actionEvent) {
127 | AdbUtils.timedCall(new Callable