├── Plugin ├── .idea │ ├── .name │ ├── copyright │ │ ├── profiles_settings.xml │ │ └── Apache2.xml │ ├── vcs.xml │ ├── modules.xml │ ├── compiler.xml │ └── misc.xml ├── android_device_controller.jar ├── resources │ └── de │ │ └── mobilej │ │ └── plugin │ │ └── adc │ │ ├── plus.png │ │ ├── Plugin.properties │ │ └── locales.txt ├── android_device_controller.iml ├── META-INF │ └── plugin.xml └── src │ └── de │ └── mobilej │ └── plugin │ └── adc │ ├── Storage.java │ ├── Configuration.java │ └── ToolWindowFactory.java ├── Enabler ├── app │ ├── .gitignore │ ├── config │ │ └── debug.keystore │ ├── src │ │ └── main │ │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ └── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── mobilej │ │ │ └── de │ │ │ └── systemproppoker │ │ │ ├── SetLocaleActivity.java │ │ │ └── SystemPropPokerActivity.java │ ├── proguard-rules.pro │ └── build.gradle ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── build.gradle ├── gradlew.bat └── gradlew ├── .gitignore ├── README.md └── LICENSE.txt /Plugin/.idea/.name: -------------------------------------------------------------------------------- 1 | ADC -------------------------------------------------------------------------------- /Enabler/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Enabler/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /Enabler/app/config/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Enabler/app/config/debug.keystore -------------------------------------------------------------------------------- /Plugin/android_device_controller.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Plugin/android_device_controller.jar -------------------------------------------------------------------------------- /Enabler/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Enabler/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Plugin/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Plugin/resources/de/mobilej/plugin/adc/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Plugin/resources/de/mobilej/plugin/adc/plus.png -------------------------------------------------------------------------------- /Enabler/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /Enabler/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Enabler/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Enabler/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Enabler/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Enabler/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Enabler/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Enabler/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Enabler/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Enabler/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Enabler/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Plugin/resources/de/mobilej/plugin/adc/Plugin.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjoernQ/adc/HEAD/Plugin/resources/de/mobilej/plugin/adc/Plugin.properties -------------------------------------------------------------------------------- /Plugin/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Enabler/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Nov 08 01:27:26 CET 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /Plugin/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Windows thumbnail db 19 | Thumbs.db 20 | 21 | # OSX files 22 | .DS_Store 23 | 24 | # Eclipse project files 25 | .classpath 26 | .project 27 | 28 | .gradle/ 29 | build/ 30 | 31 | out/ 32 | -------------------------------------------------------------------------------- /Enabler/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/bjoernquentin/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /Plugin/.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 | -------------------------------------------------------------------------------- /Plugin/.idea/copyright/Apache2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /Plugin/android_device_controller.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Enabler/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /Enabler/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Björn Quentin 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 15 | 16 | buildscript { 17 | repositories { 18 | jcenter() 19 | } 20 | dependencies { 21 | classpath 'com.android.tools.build:gradle:1.3.0' 22 | 23 | // NOTE: Do not place your application dependencies here; they belong 24 | // in the individual module build.gradle files 25 | } 26 | } 27 | 28 | allprojects { 29 | repositories { 30 | jcenter() 31 | } 32 | } 33 | 34 | task clean(type: Delete) { 35 | delete rootProject.buildDir 36 | } 37 | -------------------------------------------------------------------------------- /Enabler/app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Björn Quentin 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | apply plugin: 'com.android.application' 15 | 16 | android { 17 | compileSdkVersion 23 18 | buildToolsVersion "23.0.2" 19 | 20 | defaultConfig { 21 | applicationId "mobilej.de.systemproppoker" 22 | minSdkVersion 14 23 | targetSdkVersion 23 24 | versionCode 1 25 | versionName "1.0" 26 | } 27 | 28 | signingConfigs { 29 | debug { 30 | storeFile file('config/debug.keystore') 31 | storePassword 'android' 32 | keyAlias 'AndroidDebugKey' 33 | keyPassword 'android' 34 | } 35 | } 36 | 37 | buildTypes { 38 | release { 39 | minifyEnabled false 40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 41 | } 42 | } 43 | } 44 | 45 | dependencies { 46 | compile fileTree(dir: 'libs', include: ['*.jar']) 47 | testCompile 'junit:junit:4.12' 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADC Android Device Controller 2 | 3 | An Android Studio plugin to control your development devices and emulators. Currently you can do the following from within your IDE: 4 | 5 | - Toggle the "Show Layout Bounds" 6 | - Change the locale of the device 7 | - Open the currently active activity in your IDE (if the project contains that class) 8 | - Send text to the device (simulate keyboard input) 9 | - Clear Data (for all apps contained in current project) 10 | - Kill process of running app (great for testing "app killed in background" scenarios) 11 | 12 | More features to come. 13 | 14 | It has been used on devices running Lollipop and better. On older devices not all features might work as expected. 15 | 16 | # Installation 17 | 18 | The plugin is available from the plugins repository at https://plugins.jetbrains.com/plugin/8377?pr=androidstudio 19 | 20 | Just open _Settings_ and choose _Plugins_, _Browse repositories_ and search for _Android Device Controller_ 21 | 22 | ## License 23 | 24 | ``` 25 | Copyright 2016 Björn Quentin 26 | 27 | Licensed under the Apache License, Version 2.0 (the "License"); 28 | you may not use this file except in compliance with the License. 29 | You may obtain a copy of the License at 30 | 31 | http://www.apache.org/licenses/LICENSE-2.0 32 | 33 | Unless required by applicable law or agreed to in writing, software 34 | distributed under the License is distributed on an "AS IS" BASIS, 35 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 36 | See the License for the specific language governing permissions and 37 | limitations under the License. 38 | ``` 39 | -------------------------------------------------------------------------------- /Enabler/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 18 | 19 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Plugin/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | de.mobilej.adc.plugin.id 3 | Android Device Controller 4 | 1.0.8 5 | http://github.com/bjoernQ/adc 6 | 7 | com.intellij.modules.androidstudio 8 | org.jetbrains.android 9 | 10 | 11 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | com.intellij.modules.platform 24 | org.jetbrains.android 25 | 26 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Plugin/src/de/mobilej/plugin/adc/Storage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Björn Quentin 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | package de.mobilej.plugin.adc; 15 | 16 | import com.intellij.openapi.components.PersistentStateComponent; 17 | import com.intellij.openapi.components.State; 18 | import com.intellij.openapi.components.StoragePathMacros; 19 | import com.intellij.util.xmlb.XmlSerializerUtil; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | /** 23 | * Storage for the plugin 24 | * 25 | * Created by bjoern on 01.06.2016. 26 | */ 27 | @State(name = "ADCSettings", 28 | storages = {@com.intellij.openapi.components.Storage( 29 | file = StoragePathMacros.APP_CONFIG + "/adc_settings.xml")}) 30 | public class Storage implements PersistentStateComponent { 31 | 32 | private String lastSentText = ""; 33 | private String installedOnDevices; 34 | 35 | @Nullable 36 | @Override 37 | public Storage getState() { 38 | return this; 39 | } 40 | 41 | @Override 42 | public void loadState(Storage storage) { 43 | XmlSerializerUtil.copyBean(storage, this); 44 | } 45 | 46 | public void setLastSentText(String lastSentText) { 47 | this.lastSentText = lastSentText; 48 | } 49 | 50 | public String getLastSentText() { 51 | return lastSentText; 52 | } 53 | 54 | public String getInstalledOnDevices() { 55 | return installedOnDevices; 56 | } 57 | 58 | public void setInstalledOnDevices(String installedOnDevices) { 59 | this.installedOnDevices = installedOnDevices; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Plugin/.idea/misc.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 | -------------------------------------------------------------------------------- /Plugin/src/de/mobilej/plugin/adc/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Björn Quentin 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | package de.mobilej.plugin.adc; 15 | 16 | import com.intellij.openapi.components.ServiceManager; 17 | import com.intellij.openapi.options.Configurable; 18 | import com.intellij.openapi.options.ConfigurationException; 19 | import com.intellij.openapi.project.Project; 20 | import org.jetbrains.annotations.Nls; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | import javax.swing.*; 24 | import java.awt.event.ActionEvent; 25 | import java.awt.event.ActionListener; 26 | 27 | /** 28 | * Configuration for the plugin 29 | * 30 | * Created by bjoern on 24.07.2017. 31 | */ 32 | public class Configuration implements Configurable { 33 | 34 | private Storage storage; 35 | 36 | private boolean modified = false; 37 | private boolean clearDevicesClicked = false; 38 | 39 | public Configuration(Project project){ 40 | this.storage = ServiceManager.getService(project, Storage.class); 41 | } 42 | 43 | @Nls 44 | @Override 45 | public String getDisplayName() { 46 | return "ADC"; 47 | } 48 | 49 | @Nullable 50 | @Override 51 | public JComponent createComponent() { 52 | JComponent panel = new JPanel(); 53 | BoxLayout layout = new BoxLayout(panel, BoxLayout.PAGE_AXIS); 54 | panel.setLayout(layout); 55 | 56 | JButton clearDevices = new JButton("Clear known devices"); 57 | panel.add(clearDevices); 58 | 59 | clearDevices.addActionListener(actionEvent -> { 60 | modified = true; 61 | clearDevicesClicked = true; 62 | }); 63 | 64 | return panel; 65 | } 66 | 67 | @Override 68 | public boolean isModified() { 69 | return modified; 70 | } 71 | 72 | @Override 73 | public void apply() throws ConfigurationException { 74 | if(clearDevicesClicked) { 75 | storage.setInstalledOnDevices(""); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Enabler/app/src/main/java/mobilej/de/systemproppoker/SetLocaleActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Björn Quentin 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package mobilej.de.systemproppoker; 14 | 15 | import android.app.Activity; 16 | import android.content.res.Configuration; 17 | import android.os.Bundle; 18 | 19 | import java.lang.reflect.Field; 20 | import java.lang.reflect.Method; 21 | import java.util.Locale; 22 | 23 | /** 24 | * Created by bjoernquentin on 08.11.15. 25 | */ 26 | public class SetLocaleActivity extends Activity { 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | String lang = getIntent().getStringExtra("language"); 32 | String country = getIntent().getStringExtra("country"); 33 | SetLocaleActivity.updateLocale(new Locale(lang, country)); 34 | finish(); 35 | } 36 | 37 | 38 | public static void updateLocale(Locale locale) { 39 | try { 40 | System.out.println("in"); 41 | Method activityManagerNativegetDefaultMethod = Class.forName("android.app.ActivityManagerNative").getDeclaredMethod("getDefault"); 42 | Object am = activityManagerNativegetDefaultMethod.invoke(null); 43 | 44 | Configuration config = (Configuration) am.getClass().getDeclaredMethod("getConfiguration").invoke(am); 45 | 46 | config.locale = locale; 47 | 48 | // indicate this isn't some passing default - the user wants this remembered 49 | Field userSetLocaleField = Configuration.class.getDeclaredField("userSetLocale"); 50 | userSetLocaleField.set(config, true); 51 | 52 | am.getClass().getDeclaredMethod("updateConfiguration", Configuration.class).invoke(am, config); 53 | // Trigger the dirty bit for the Settings Provider. 54 | // BackupManager.dataChanged("com.android.providers.settings"); 55 | 56 | } catch (Exception e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Enabler/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /Enabler/app/src/main/java/mobilej/de/systemproppoker/SystemPropPokerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Björn Quentin 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package mobilej.de.systemproppoker; 14 | 15 | import android.app.Activity; 16 | import android.os.AsyncTask; 17 | import android.os.Bundle; 18 | import android.os.IBinder; 19 | import android.os.Parcel; 20 | import android.os.RemoteException; 21 | import android.util.Log; 22 | 23 | import java.lang.reflect.Method; 24 | 25 | /** 26 | * Created by bjoernquentin on 08.11.15. 27 | */ 28 | public class SystemPropPokerActivity extends Activity { 29 | 30 | private static int SYSPROPS_TRANSACTION = ('_' << 24) | ('S' << 16) | ('P' << 8) | 'R'; 31 | private static final String TAG = "poker"; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | new SystemPropPoker().execute(); 37 | finish(); 38 | } 39 | 40 | static class SystemPropPoker extends AsyncTask { 41 | 42 | @Override 43 | protected Void doInBackground(Void... params) { 44 | try { 45 | String[] services; 46 | try { 47 | Method listServicesMethod = Class.forName("android.os.ServiceManager").getDeclaredMethod("listServices"); 48 | services = (String[]) listServicesMethod.invoke(null); 49 | } catch (Exception e) { 50 | return null; 51 | } 52 | 53 | if (services == null || services.length == 0) { 54 | services = new String[]{"phone", "iphonesubinfo", "simphonebook", "isms", "media_router", "print", "assetatlas", "dreams", "commontime_management", "samplingprofiler", "diskstats", "appwidget", "backup", "uimode", "serial", "usb", "audio", "wallpaper", "dropbox", "search", "country_detector", "location", "notification", "updatelock", "servicediscovery", "connectivity", "wifi", "wifip2p", "netpolicy", "netstats", "textservices", "network_management", "clipboard", "statusbar", "device_policy", "lock_settings", "mount", "accessibility", "input_method", "input", "window", "alarm", "consumer_ir", "vibrator", "battery", "hardware", "content", "account", "user", "entropy", "permission", "cpuinfo", "dbinfo", "gfxinfo", "meminfo", "procstats", "activity", "package", "scheduling_policy", "telephony.registry", "display", "appops", "usagestats", "batterystats", "power", "sensorservice", "batterypropreg", "media.audio_policy", "media.camera", "media.player", "media.audio_flinger", "drm.drmManager", "SurfaceFlinger", "android.security.keystore"}; // TODO find the right services ... 55 | } 56 | 57 | for (String service : services) { 58 | Method checkServiceMethod = Class.forName("android.os.ServiceManager").getDeclaredMethod("checkService", String.class); 59 | IBinder obj = (IBinder) checkServiceMethod.invoke(null, service); 60 | if (obj != null) { 61 | Parcel data = Parcel.obtain(); 62 | try { 63 | obj.transact(SYSPROPS_TRANSACTION, data, null, 0); 64 | } catch (RemoteException e) { 65 | } catch (Exception e) { 66 | Log.i(TAG, "Someone wrote a bad service '" + service 67 | + "' that doesn't like to be poked: " + e); 68 | } 69 | data.recycle(); 70 | } 71 | } 72 | 73 | } catch (Exception e) { 74 | return null; 75 | } 76 | 77 | return null; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Enabler/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2018 Björn Quentin 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Plugin/resources/de/mobilej/plugin/adc/locales.txt: -------------------------------------------------------------------------------- 1 | de_DE [German (Germany)] 2 | en_US [English (United States)] 3 | en_XA [English (Pseudo Localisation ltr)] 4 | ar_XB [Arabic (Pseudo Localisation rtl)] 5 | af_NA [Afrikaans (Namibia)] 6 | af_ZA [Afrikaans (South Africa)] 7 | agq_CM [Aghem (Cameroon)] 8 | ak_GH [Akan (Ghana)] 9 | am_ET [Amharic (Ethiopia)] 10 | ar_001 [Arabic (World)] 11 | ar_AE [Arabic (United Arab Emirates)] 12 | ar_BH [Arabic (Bahrain)] 13 | ar_DJ [Arabic (Djibouti)] 14 | ar_DZ [Arabic (Algeria)] 15 | ar_EG [Arabic (Egypt)] 16 | ar_EH [Arabic (Western Sahara)] 17 | ar_ER [Arabic (Eritrea)] 18 | ar_IL [Arabic (Israel)] 19 | ar_IQ [Arabic (Iraq)] 20 | ar_JO [Arabic (Jordan)] 21 | ar_KM [Arabic (Comoros)] 22 | ar_KW [Arabic (Kuwait)] 23 | ar_LB [Arabic (Lebanon)] 24 | ar_LY [Arabic (Libya)] 25 | ar_MA [Arabic (Morocco)] 26 | ar_MR [Arabic (Mauritania)] 27 | ar_OM [Arabic (Oman)] 28 | ar_PS [Arabic (Palestine)] 29 | ar_QA [Arabic (Qatar)] 30 | ar_SA [Arabic (Saudi Arabia)] 31 | ar_SD [Arabic (Sudan)] 32 | ar_SO [Arabic (Somalia)] 33 | ar_SS [Arabic (South Sudan)] 34 | ar_SY [Arabic (Syria)] 35 | ar_TD [Arabic (Chad)] 36 | ar_TN [Arabic (Tunisia)] 37 | ar_YE [Arabic (Yemen)] 38 | as_IN [Assamese (India)] 39 | asa_TZ [Asu (Tanzania)] 40 | az_AZ [Azerbaijani (Cyrillic,Azerbaijan)] 41 | az_AZ [Azerbaijani (Latin,Azerbaijan)] 42 | bas_CM [Basaa (Cameroon)] 43 | be_BY [Belarusian (Belarus)] 44 | bem_ZM [Bemba (Zambia)] 45 | bez_TZ [Bena (Tanzania)] 46 | bg_BG [Bulgarian (Bulgaria)] 47 | bm_ML [Bambara (Mali)] 48 | bn_BD [Bengali (Bangladesh)] 49 | bn_IN [Bengali (India)] 50 | bo_CN [Tibetan (China)] 51 | bo_IN [Tibetan (India)] 52 | br_FR [Breton (France)] 53 | brx_IN [Bodo (India)] 54 | bs_BA [Bosnian (Cyrillic,Bosnia and Herzegovina)] 55 | bs_BA [Bosnian (Latin,Bosnia and Herzegovina)] 56 | ca_AD [Catalan (Andorra)] 57 | ca_ES [Catalan (Spain)] 58 | ca_FR [Catalan (France)] 59 | ca_IT [Catalan (Italy)] 60 | cgg_UG [Chiga (Uganda)] 61 | chr_US [Cherokee (United States)] 62 | cs_CZ [Czech (Czech Republic)] 63 | cy_GB [Welsh (United Kingdom)] 64 | da_DK [Danish (Denmark)] 65 | da_GL [Danish (Greenland)] 66 | dav_KE [Taita (Kenya)] 67 | de_AT [German (Austria)] 68 | de_BE [German (Belgium)] 69 | de_CH [German (Switzerland)] 70 | de_DE [German (Germany)] 71 | de_LI [German (Liechtenstein)] 72 | de_LU [German (Luxembourg)] 73 | dje_NE [Zarma (Niger)] 74 | dua_CM [Duala (Cameroon)] 75 | dyo_SN [Jola-Fonyi (Senegal)] 76 | dz_BT [Dzongkha (Bhutan)] 77 | ebu_KE [Embu (Kenya)] 78 | ee_GH [Ewe (Ghana)] 79 | ee_TG [Ewe (Togo)] 80 | el_CY [Greek (Cyprus)] 81 | el_GR [Greek (Greece)] 82 | en_001 [English (World)] 83 | en_150 [English (Europe)] 84 | en_AG [English (Antigua and Barbuda)] 85 | en_AI [English (Anguilla)] 86 | en_AS [English (American Samoa)] 87 | en_AU [English (Australia)] 88 | en_BB [English (Barbados)] 89 | en_BE [English (Belgium)] 90 | en_BM [English (Bermuda)] 91 | en_BS [English (Bahamas)] 92 | en_BW [English (Botswana)] 93 | en_BZ [English (Belize)] 94 | en_CA [English (Canada)] 95 | en_CC [English (Cocos (Keeling) Islands)] 96 | en_CK [English (Cook Islands)] 97 | en_CM [English (Cameroon)] 98 | en_CX [English (Christmas Island)] 99 | en_DG [English (Diego Garcia)] 100 | en_DM [English (Dominica)] 101 | en_ER [English (Eritrea)] 102 | en_FJ [English (Fiji)] 103 | en_FK [English (Falkland Islands (Islas Malvinas))] 104 | en_FM [English (Micronesia)] 105 | en_GB [English (United Kingdom)] 106 | en_GD [English (Grenada)] 107 | en_GG [English (Guernsey)] 108 | en_GH [English (Ghana)] 109 | en_GI [English (Gibraltar)] 110 | en_GM [English (Gambia)] 111 | en_GU [English (Guam)] 112 | en_GY [English (Guyana)] 113 | en_HK [English (Hong Kong)] 114 | en_IE [English (Ireland)] 115 | en_IM [English (Isle of Man)] 116 | en_IN [English (India)] 117 | en_IO [English (British Indian Ocean Territory)] 118 | en_JE [English (Jersey)] 119 | en_JM [English (Jamaica)] 120 | en_KE [English (Kenya)] 121 | en_KI [English (Kiribati)] 122 | en_KN [English (Saint Kitts and Nevis)] 123 | en_KY [English (Cayman Islands)] 124 | en_LC [English (Saint Lucia)] 125 | en_LR [English (Liberia)] 126 | en_LS [English (Lesotho)] 127 | en_MG [English (Madagascar)] 128 | en_MH [English (Marshall Islands)] 129 | en_MO [English (Macau)] 130 | en_MP [English (Northern Mariana Islands)] 131 | en_MS [English (Montserrat)] 132 | en_MT [English (Malta)] 133 | en_MU [English (Mauritius)] 134 | en_MW [English (Malawi)] 135 | en_NA [English (Namibia)] 136 | en_NF [English (Norfolk Island)] 137 | en_NG [English (Nigeria)] 138 | en_NR [English (Nauru)] 139 | en_NU [English (Niue)] 140 | en_NZ [English (New Zealand)] 141 | en_PG [English (Papua New Guinea)] 142 | en_PH [English (Philippines)] 143 | en_PK [English (Pakistan)] 144 | en_PN [English (Pitcairn Islands)] 145 | en_PR [English (Puerto Rico)] 146 | en_PW [English (Palau)] 147 | en_RW [English (Rwanda)] 148 | en_SB [English (Solomon Islands)] 149 | en_SC [English (Seychelles)] 150 | en_SD [English (Sudan)] 151 | en_SG [English (Singapore)] 152 | en_SH [English (Saint Helena)] 153 | en_SL [English (Sierra Leone)] 154 | en_SS [English (South Sudan)] 155 | en_SX [English (Sint Maarten)] 156 | en_SZ [English (Swaziland)] 157 | en_TC [English (Turks and Caicos Islands)] 158 | en_TK [English (Tokelau)] 159 | en_TO [English (Tonga)] 160 | en_TT [English (Trinidad and Tobago)] 161 | en_TV [English (Tuvalu)] 162 | en_TZ [English (Tanzania)] 163 | en_UG [English (Uganda)] 164 | en_UM [English (U.S. Outlying Islands)] 165 | en_US [English (United States)] 166 | en_US [English (United States,Computer)] 167 | en_VC [English (St. Vincent & Grenadines)] 168 | en_VG [English (British Virgin Islands)] 169 | en_VI [English (U.S. Virgin Islands)] 170 | en_VU [English (Vanuatu)] 171 | en_WS [English (Samoa)] 172 | en_ZA [English (South Africa)] 173 | en_ZM [English (Zambia)] 174 | en_ZW [English (Zimbabwe)] 175 | es_419 [Spanish (Latin America)] 176 | es_AR [Spanish (Argentina)] 177 | es_BO [Spanish (Bolivia)] 178 | es_CL [Spanish (Chile)] 179 | es_CO [Spanish (Colombia)] 180 | es_CR [Spanish (Costa Rica)] 181 | es_CU [Spanish (Cuba)] 182 | es_DO [Spanish (Dominican Republic)] 183 | es_EA [Spanish (Ceuta and Melilla)] 184 | es_EC [Spanish (Ecuador)] 185 | es_ES [Spanish (Spain)] 186 | es_GQ [Spanish (Equatorial Guinea)] 187 | es_GT [Spanish (Guatemala)] 188 | es_HN [Spanish (Honduras)] 189 | es_IC [Spanish (Canary Islands)] 190 | es_MX [Spanish (Mexico)] 191 | es_NI [Spanish (Nicaragua)] 192 | es_PA [Spanish (Panama)] 193 | es_PE [Spanish (Peru)] 194 | es_PH [Spanish (Philippines)] 195 | es_PR [Spanish (Puerto Rico)] 196 | es_PY [Spanish (Paraguay)] 197 | es_SV [Spanish (El Salvador)] 198 | es_US [Spanish (United States)] 199 | es_UY [Spanish (Uruguay)] 200 | es_VE [Spanish (Venezuela)] 201 | et_EE [Estonian (Estonia)] 202 | eu_ES [Basque (Spain)] 203 | ewo_CM [Ewondo (Cameroon)] 204 | fa_AF [Persian (Afghanistan)] 205 | fa_IR [Persian (Iran)] 206 | ff_SN [Fulah (Senegal)] 207 | fi_FI [Finnish (Finland)] 208 | fil_PH [Filipino (Philippines)] 209 | fo_FO [Faroese (Faroe Islands)] 210 | fr_BE [French (Belgium)] 211 | fr_BF [French (Burkina Faso)] 212 | fr_BI [French (Burundi)] 213 | fr_BJ [French (Benin)] 214 | fr_BL [French (Saint Barthélemy)] 215 | fr_CA [French (Canada)] 216 | fr_CD [French (Congo (DRC))] 217 | fr_CF [French (Central African Republic)] 218 | fr_CG [French (Congo (Republic))] 219 | fr_CH [French (Switzerland)] 220 | fr_CI [French (Côte d’Ivoire)] 221 | fr_CM [French (Cameroon)] 222 | fr_DJ [French (Djibouti)] 223 | fr_DZ [French (Algeria)] 224 | fr_FR [French (France)] 225 | fr_GA [French (Gabon)] 226 | fr_GF [French (French Guiana)] 227 | fr_GN [French (Guinea)] 228 | fr_GP [French (Guadeloupe)] 229 | fr_GQ [French (Equatorial Guinea)] 230 | fr_HT [French (Haiti)] 231 | fr_KM [French (Comoros)] 232 | fr_LU [French (Luxembourg)] 233 | fr_MA [French (Morocco)] 234 | fr_MC [French (Monaco)] 235 | fr_MF [French (Saint Martin)] 236 | fr_MG [French (Madagascar)] 237 | fr_ML [French (Mali)] 238 | fr_MQ [French (Martinique)] 239 | fr_MR [French (Mauritania)] 240 | fr_MU [French (Mauritius)] 241 | fr_NC [French (New Caledonia)] 242 | fr_NE [French (Niger)] 243 | fr_PF [French (French Polynesia)] 244 | fr_PM [French (Saint Pierre and Miquelon)] 245 | fr_RE [French (Réunion)] 246 | fr_RW [French (Rwanda)] 247 | fr_SC [French (Seychelles)] 248 | fr_SN [French (Senegal)] 249 | fr_SY [French (Syria)] 250 | fr_TD [French (Chad)] 251 | fr_TG [French (Togo)] 252 | fr_TN [French (Tunisia)] 253 | fr_VU [French (Vanuatu)] 254 | fr_WF [French (Wallis and Futuna)] 255 | fr_YT [French (Mayotte)] 256 | ga_IE [Irish (Ireland)] 257 | gl_ES [Galician (Spain)] 258 | gsw_CH [Swiss German (Switzerland)] 259 | gsw_LI [Swiss German (Liechtenstein)] 260 | gu_IN [Gujarati (India)] 261 | guz_KE [Gusii (Kenya)] 262 | gv_IM [Manx (Isle of Man)] 263 | ha_GH [Hausa (Latin,Ghana)] 264 | ha_NE [Hausa (Latin,Niger)] 265 | ha_NG [Hausa (Latin,Nigeria)] 266 | haw_US [Hawaiian (United States)] 267 | iw_IL [Hebrew (Israel)] 268 | hi_IN [Hindi (India)] 269 | hr_BA [Croatian (Bosnia and Herzegovina)] 270 | hr_HR [Croatian (Croatia)] 271 | hu_HU [Hungarian (Hungary)] 272 | hy_AM [Armenian (Armenia)] 273 | in_ID [Indonesian (Indonesia)] 274 | ig_NG [Igbo (Nigeria)] 275 | ii_CN [Sichuan Yi (China)] 276 | is_IS [Icelandic (Iceland)] 277 | it_CH [Italian (Switzerland)] 278 | it_IT [Italian (Italy)] 279 | it_SM [Italian (San Marino)] 280 | ja_JP [Japanese (Japan)] 281 | jgo_CM [Ngomba (Cameroon)] 282 | jmc_TZ [Machame (Tanzania)] 283 | ka_GE [Georgian (Georgia)] 284 | kab_DZ [Kabyle (Algeria)] 285 | kam_KE [Kamba (Kenya)] 286 | kde_TZ [Makonde (Tanzania)] 287 | kea_CV [Kabuverdianu (Cape Verde)] 288 | khq_ML [Koyra Chiini (Mali)] 289 | ki_KE [Kikuyu (Kenya)] 290 | kk_KZ [Kazakh (Cyrillic,Kazakhstan)] 291 | kkj_CM [Kako (Cameroon)] 292 | kl_GL [Kalaallisut (Greenland)] 293 | kln_KE [Kalenjin (Kenya)] 294 | km_KH [Khmer (Cambodia)] 295 | kn_IN [Kannada (India)] 296 | ko_KP [Korean (North Korea)] 297 | ko_KR [Korean (South Korea)] 298 | kok_IN [Konkani (India)] 299 | ks_IN [Kashmiri (Arabic,India)] 300 | ksb_TZ [Shambala (Tanzania)] 301 | ksf_CM [Bafia (Cameroon)] 302 | kw_GB [Cornish (United Kingdom)] 303 | ky_KG [Kyrgyz (Cyrillic,Kyrgyzstan)] 304 | lag_TZ [Langi (Tanzania)] 305 | lg_UG [Ganda (Uganda)] 306 | lkt_US [Lakota (United States)] 307 | ln_AO [Lingala (Angola)] 308 | ln_CD [Lingala (Congo (DRC))] 309 | ln_CF [Lingala (Central African Republic)] 310 | ln_CG [Lingala (Congo (Republic))] 311 | lo_LA [Lao (Laos)] 312 | lt_LT [Lithuanian (Lithuania)] 313 | lu_CD [Luba-Katanga (Congo (DRC))] 314 | luo_KE [Luo (Kenya)] 315 | luy_KE [Luyia (Kenya)] 316 | lv_LV [Latvian (Latvia)] 317 | mas_KE [Masai (Kenya)] 318 | mas_TZ [Masai (Tanzania)] 319 | mer_KE [Meru (Kenya)] 320 | mfe_MU [Morisyen (Mauritius)] 321 | mg_MG [Malagasy (Madagascar)] 322 | mgh_MZ [Makhuwa-Meetto (Mozambique)] 323 | mgo_CM [Meta' (Cameroon)] 324 | mk_MK [Macedonian (Macedonia (FYROM))] 325 | ml_IN [Malayalam (India)] 326 | mn_MN [Mongolian (Cyrillic,Mongolia)] 327 | mr_IN [Marathi (India)] 328 | ms_BN [Malay (Latin,Brunei)] 329 | ms_MY [Malay (Latin,Malaysia)] 330 | ms_SG [Malay (Latin,Singapore)] 331 | mt_MT [Maltese (Malta)] 332 | mua_CM [Mundang (Cameroon)] 333 | my_MM [Burmese (Myanmar (Burma))] 334 | naq_NA [Nama (Namibia)] 335 | nb_NO [Norwegian Bokmål (Norway)] 336 | nb_SJ [Norwegian Bokmål (Svalbard and Jan Mayen)] 337 | nd_ZW [North Ndebele (Zimbabwe)] 338 | ne_IN [Nepali (India)] 339 | ne_NP [Nepali (Nepal)] 340 | nl_AW [Dutch (Aruba)] 341 | nl_BE [Dutch (Belgium)] 342 | nl_BQ [Dutch (Caribbean Netherlands)] 343 | nl_CW [Dutch (Curaçao)] 344 | nl_NL [Dutch (Netherlands)] 345 | nl_SR [Dutch (Suriname)] 346 | nl_SX [Dutch (Sint Maarten)] 347 | nmg_CM [Kwasio (Cameroon)] 348 | nn_NO [Norwegian Nynorsk (Norway)] 349 | nnh_CM [Ngiemboon (Cameroon)] 350 | nus_SD [Nuer (Sudan)] 351 | nyn_UG [Nyankole (Uganda)] 352 | om_ET [Oromo (Ethiopia)] 353 | om_KE [Oromo (Kenya)] 354 | or_IN [Oriya (India)] 355 | pa_PK [Punjabi (Arabic,Pakistan)] 356 | pa_IN [Punjabi (Gurmukhi,India)] 357 | pl_PL [Polish (Poland)] 358 | ps_AF [Pashto (Afghanistan)] 359 | pt_AO [Portuguese (Angola)] 360 | pt_BR [Portuguese (Brazil)] 361 | pt_CV [Portuguese (Cape Verde)] 362 | pt_GW [Portuguese (Guinea-Bissau)] 363 | pt_MO [Portuguese (Macau)] 364 | pt_MZ [Portuguese (Mozambique)] 365 | pt_PT [Portuguese (Portugal)] 366 | pt_ST [Portuguese (São Tomé and Príncipe)] 367 | pt_TL [Portuguese (Timor-Leste)] 368 | rm_CH [Romansh (Switzerland)] 369 | rn_BI [Rundi (Burundi)] 370 | ro_MD [Romanian (Moldova)] 371 | ro_RO [Romanian (Romania)] 372 | rof_TZ [Rombo (Tanzania)] 373 | ru_BY [Russian (Belarus)] 374 | ru_KG [Russian (Kyrgyzstan)] 375 | ru_KZ [Russian (Kazakhstan)] 376 | ru_MD [Russian (Moldova)] 377 | ru_RU [Russian (Russia)] 378 | ru_UA [Russian (Ukraine)] 379 | rw_RW [Kinyarwanda (Rwanda)] 380 | rwk_TZ [Rwa (Tanzania)] 381 | saq_KE [Samburu (Kenya)] 382 | sbp_TZ [Sangu (Tanzania)] 383 | seh_MZ [Sena (Mozambique)] 384 | ses_ML [Koyraboro Senni (Mali)] 385 | sg_CF [Sango (Central African Republic)] 386 | shi_MA [Tachelhit (Latin,Morocco)] 387 | shi_MA [Tachelhit (Tifinagh,Morocco)] 388 | si_LK [Sinhala (Sri Lanka)] 389 | sk_SK [Slovak (Slovakia)] 390 | sl_SI [Slovenian (Slovenia)] 391 | sn_ZW [Shona (Zimbabwe)] 392 | so_DJ [Somali (Djibouti)] 393 | so_ET [Somali (Ethiopia)] 394 | so_KE [Somali (Kenya)] 395 | so_SO [Somali (Somalia)] 396 | sq_AL [Albanian (Albania)] 397 | sq_MK [Albanian (Macedonia (FYROM))] 398 | sq_XK [Albanian (Kosovo)] 399 | sr_BA [Serbian (Cyrillic,Bosnia and Herzegovina)] 400 | sr_ME [Serbian (Cyrillic,Montenegro)] 401 | sr_RS [Serbian (Cyrillic,Serbia)] 402 | sr_XK [Serbian (Cyrillic,Kosovo)] 403 | sr_BA [Serbian (Latin,Bosnia and Herzegovina)] 404 | sr_ME [Serbian (Latin,Montenegro)] 405 | sr_RS [Serbian (Latin,Serbia)] 406 | sr_XK [Serbian (Latin,Kosovo)] 407 | sv_AX [Swedish (Åland Islands)] 408 | sv_FI [Swedish (Finland)] 409 | sv_SE [Swedish (Sweden)] 410 | sw_KE [Swahili (Kenya)] 411 | sw_TZ [Swahili (Tanzania)] 412 | sw_UG [Swahili (Uganda)] 413 | swc_CD [Congo Swahili (Congo (DRC))] 414 | ta_IN [Tamil (India)] 415 | ta_LK [Tamil (Sri Lanka)] 416 | ta_MY [Tamil (Malaysia)] 417 | ta_SG [Tamil (Singapore)] 418 | te_IN [Telugu (India)] 419 | teo_KE [Teso (Kenya)] 420 | teo_UG [Teso (Uganda)] 421 | th_TH [Thai (Thailand)] 422 | ti_ER [Tigrinya (Eritrea)] 423 | ti_ET [Tigrinya (Ethiopia)] 424 | to_TO [Tongan (Tonga)] 425 | tr_CY [Turkish (Cyprus)] 426 | tr_TR [Turkish (Turkey)] 427 | twq_NE [Tasawaq (Niger)] 428 | tzm_MA [Central Atlas Tamazight (Latin,Morocco)] 429 | ug_CN [Uyghur (Arabic,China)] 430 | uk_UA [Ukrainian (Ukraine)] 431 | ur_IN [Urdu (India)] 432 | ur_PK [Urdu (Pakistan)] 433 | uz_AF [Uzbek (Arabic,Afghanistan)] 434 | uz_UZ [Uzbek (Cyrillic,Uzbekistan)] 435 | uz_UZ [Uzbek (Latin,Uzbekistan)] 436 | vai_LR [Vai (Latin,Liberia)] 437 | vai_LR [Vai (Vai,Liberia)] 438 | vi_VN [Vietnamese (Vietnam)] 439 | vun_TZ [Vunjo (Tanzania)] 440 | xog_UG [Soga (Uganda)] 441 | yav_CM [Yangben (Cameroon)] 442 | yo_BJ [Yoruba (Benin)] 443 | yo_NG [Yoruba (Nigeria)] 444 | zgh_MA [Standard Moroccan Tamazight (Morocco)] 445 | zh_CN [Chinese (Simplified Han,China)] 446 | zh_HK [Chinese (Simplified Han,Hong Kong)] 447 | zh_MO [Chinese (Simplified Han,Macau)] 448 | zh_SG [Chinese (Simplified Han,Singapore)] 449 | zh_HK [Chinese (Traditional Han,Hong Kong)] 450 | zh_MO [Chinese (Traditional Han,Macau)] 451 | zh_TW [Chinese (Traditional Han,Taiwan)] 452 | zu_ZA [Zulu (South Africa)] 453 | -------------------------------------------------------------------------------- /Plugin/src/de/mobilej/plugin/adc/ToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Björn Quentin 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | package de.mobilej.plugin.adc; 15 | 16 | import com.android.ddmlib.*; 17 | import com.android.tools.idea.model.AndroidModel; 18 | import com.intellij.facet.ProjectFacetManager; 19 | import com.intellij.openapi.application.ApplicationManager; 20 | import com.intellij.openapi.components.ServiceManager; 21 | import com.intellij.openapi.diagnostic.Logger; 22 | import com.intellij.openapi.fileEditor.FileEditorManager; 23 | import com.intellij.openapi.fileEditor.OpenFileDescriptor; 24 | import com.intellij.openapi.progress.ProgressManager; 25 | import com.intellij.openapi.project.Project; 26 | import com.intellij.openapi.ui.ComboBox; 27 | import com.intellij.openapi.ui.Messages; 28 | import com.intellij.openapi.vfs.VirtualFile; 29 | import com.intellij.openapi.wm.ToolWindow; 30 | import com.intellij.psi.JavaPsiFacade; 31 | import com.intellij.psi.PsiClass; 32 | import com.intellij.psi.search.GlobalSearchScope; 33 | import com.intellij.ui.components.JBCheckBox; 34 | import com.intellij.ui.content.Content; 35 | import com.intellij.ui.content.ContentFactory; 36 | import org.jetbrains.android.facet.AndroidFacet; 37 | import org.jetbrains.android.facet.AndroidFacetConfiguration; 38 | import org.jetbrains.android.sdk.AndroidSdkUtils; 39 | import org.jetbrains.annotations.NotNull; 40 | import org.xmlpull.v1.XmlPullParser; 41 | import org.xmlpull.v1.XmlPullParserException; 42 | import org.xmlpull.v1.XmlPullParserFactory; 43 | import org.xmlpull.v1.util.XmlPullUtil; 44 | 45 | import javax.swing.*; 46 | import java.awt.*; 47 | import java.awt.event.ActionListener; 48 | import java.io.*; 49 | import java.text.MessageFormat; 50 | import java.util.*; 51 | import java.util.List; 52 | import java.util.concurrent.ExecutionException; 53 | import java.util.regex.Pattern; 54 | 55 | /** 56 | * Android Device Controller Plugin for Android Studio 57 | */ 58 | public class ToolWindowFactory implements com.intellij.openapi.wm.ToolWindowFactory { 59 | 60 | private static ResourceBundle resourceBundle = ResourceBundle.getBundle("de.mobilej.plugin.adc.Plugin"); 61 | 62 | private static LocaleData[] LOCALES; 63 | 64 | static { 65 | ArrayList data = new ArrayList<>(); 66 | BufferedReader lr = null; 67 | try { 68 | InputStream is = ToolWindowFactory.class.getResourceAsStream("/de/mobilej/plugin/adc/locales.txt"); 69 | lr = new BufferedReader(new InputStreamReader(is, "UTF-8")); 70 | String line = null; 71 | while ((line = lr.readLine()) != null) { 72 | if (line.indexOf("_") > 0 && line.indexOf("[") > 0) { 73 | String lang = line.substring(0, line.indexOf("_")); 74 | String cntry = line.substring(line.indexOf("_") + 1, line.indexOf(" ")); 75 | String desc = line.substring(line.indexOf("[") + 1, line.length() - 1); 76 | LocaleData ld = new LocaleData(desc, lang, cntry); 77 | data.add(ld); 78 | } 79 | } 80 | } catch (IOException ioe) { 81 | // ignored 82 | } 83 | LOCALES = data.toArray(new LocaleData[data.size()]); 84 | } 85 | 86 | private ComboBox devices; 87 | private AndroidDebugBridge adBridge; 88 | private JButton inputOnDeviceButton; 89 | private JButton clearDataButton; 90 | private JButton killProcessButton; 91 | 92 | 93 | private static class StringShellOutputReceiver implements IShellOutputReceiver { 94 | private StringBuffer result = new StringBuffer(); 95 | 96 | void reset() { 97 | result.delete(0, result.length()); 98 | } 99 | 100 | String getResult() { 101 | return result.toString(); 102 | } 103 | 104 | @Override 105 | public void addOutput(byte[] bytes, int i, int i1) { 106 | try { 107 | result.append(new String(bytes, i, i1, "UTF-8")); 108 | } catch (UnsupportedEncodingException e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | 113 | @Override 114 | public void flush() { 115 | } 116 | 117 | @Override 118 | public boolean isCancelled() { 119 | return false; 120 | } 121 | } 122 | 123 | private StringShellOutputReceiver rcv = new StringShellOutputReceiver(); 124 | 125 | private AndroidDebugBridge.IDeviceChangeListener deviceChangeListener = new AndroidDebugBridge.IDeviceChangeListener() { 126 | @Override 127 | public void deviceConnected(IDevice iDevice) { 128 | updateDeviceComboBox(); 129 | } 130 | 131 | @Override 132 | public void deviceDisconnected(IDevice iDevice) { 133 | updateDeviceComboBox(); 134 | } 135 | 136 | @Override 137 | public void deviceChanged(IDevice iDevice, int i) { 138 | } 139 | }; 140 | 141 | private ActionListener deviceSelectedListener = e -> updateFromDevice(); 142 | 143 | private JBCheckBox showLayoutBounds; 144 | private ComboBox localeChooser; 145 | private boolean userAction = false; 146 | private JButton goToActivityButton; 147 | 148 | private final Storage storage = ServiceManager.getService(Storage.class); 149 | 150 | public ToolWindowFactory() { 151 | } 152 | 153 | // Create the tool window content. 154 | public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { 155 | ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); 156 | JPanel framePanel = createPanel(project); 157 | disableAll(); 158 | 159 | AndroidDebugBridge adb = AndroidSdkUtils.getDebugBridge(project); 160 | if (adb == null) { 161 | return; 162 | } 163 | 164 | if(adb.isConnected()){ 165 | ToolWindowFactory.this.adBridge = adb; 166 | Logger.getInstance(ToolWindowFactory.class).info("Successfully obtained debug bridge"); 167 | AndroidDebugBridge.addDeviceChangeListener(deviceChangeListener); 168 | updateDeviceComboBox(); 169 | } else { 170 | Logger.getInstance(ToolWindowFactory.class).info("Unable to obtain debug bridge"); 171 | String msg = MessageFormat.format(resourceBundle.getString("error.message.adb"), ""); 172 | Messages.showErrorDialog(msg, resourceBundle.getString("error.title.adb")); 173 | } 174 | 175 | Content content = contentFactory.createContent(framePanel, "", false); 176 | toolWindow.getContentManager().addContent(content); 177 | } 178 | 179 | @NotNull 180 | private JPanel createPanel(@NotNull Project project) { 181 | // Create Panel and Content 182 | JPanel panel = new JPanel(new GridBagLayout()); 183 | GridBagConstraints c = new GridBagConstraints(); 184 | c.fill = GridBagConstraints.NONE; 185 | c.anchor = GridBagConstraints.LINE_START; 186 | c.insets = new Insets(6,0,0,0); 187 | 188 | devices = new ComboBox(new String[]{resourceBundle.getString("device.none")}); 189 | 190 | c.gridx = 0; 191 | c.gridy = 0; 192 | panel.add(new JLabel("Device"), c); 193 | c.gridx = 1; 194 | c.gridy = 0; 195 | panel.add(devices, c); 196 | 197 | 198 | showLayoutBounds = new JBCheckBox(resourceBundle.getString("show.layout.bounds")); 199 | c.gridx = 0; 200 | c.gridy = 1; 201 | c.gridwidth = 2; 202 | panel.add(showLayoutBounds, c); 203 | 204 | showLayoutBounds.addActionListener(e -> { 205 | final String what = showLayoutBounds.isSelected() ? "true" : "\"\""; 206 | final String cmd = "setprop debug.layout " + what; 207 | 208 | ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { 209 | ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); 210 | userAction = true; 211 | executeShellCommand(cmd, true); 212 | userAction = false; 213 | }, resourceBundle.getString("setting.values.title"), false, null); 214 | }); 215 | 216 | 217 | localeChooser = new ComboBox(LOCALES); 218 | c.gridx = 0; 219 | c.gridy = 2; 220 | c.gridwidth = 2; 221 | panel.add(localeChooser, c); 222 | 223 | localeChooser.addActionListener(e -> { 224 | final LocaleData ld = (LocaleData) localeChooser.getSelectedItem(); 225 | if (ld == null) { 226 | return; 227 | } 228 | 229 | ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { 230 | ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); 231 | userAction = true; 232 | executeShellCommand("am start -a SETMYLOCALE --es language " + ld.language + " --es country " + ld.county, false); 233 | userAction = false; 234 | }, resourceBundle.getString("setting.values.title"), false, null); 235 | }); 236 | 237 | 238 | goToActivityButton = new JButton(resourceBundle.getString("button.goto_activity")); 239 | c.gridx = 0; 240 | c.gridy = 3; 241 | c.gridwidth = 2; 242 | c.fill = GridBagConstraints.HORIZONTAL; 243 | panel.add(goToActivityButton, c); 244 | 245 | goToActivityButton.addActionListener(e -> ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { 246 | ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); 247 | userAction = true; 248 | final String result = executeShellCommand("dumpsys activity top", false); 249 | userAction = false; 250 | 251 | if (result == null) { 252 | return; 253 | } 254 | 255 | ApplicationManager.getApplication().invokeLater(() -> { 256 | 257 | String activity = result.substring(result.lastIndexOf("ACTIVITY ") + 9); 258 | activity = activity.substring(0, activity.indexOf(" ")); 259 | String pkg = activity.substring(0, activity.indexOf("/")); 260 | String clz = activity.substring(activity.indexOf("/") + 1); 261 | if (clz.startsWith(".")) { 262 | clz = pkg + clz; 263 | } 264 | 265 | GlobalSearchScope scope = GlobalSearchScope.allScope(project); 266 | PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(clz, scope); 267 | 268 | if (psiClass != null) { 269 | FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); 270 | //Open the file containing the class 271 | VirtualFile vf = psiClass.getContainingFile().getVirtualFile(); 272 | //Jump there 273 | new OpenFileDescriptor(project, vf, 1, 0).navigateInEditor(project, false); 274 | } else { 275 | Messages.showMessageDialog(project, clz, resourceBundle.getString("error.class_not_found"), Messages.getWarningIcon()); 276 | return; 277 | } 278 | 279 | }); 280 | 281 | }, resourceBundle.getString("setting.values.title"), false, null)); 282 | 283 | 284 | inputOnDeviceButton = new JButton(resourceBundle.getString("button.input_on_device")); 285 | c.gridx = 0; 286 | c.gridy = 4; 287 | c.gridwidth = 2; 288 | c.fill = GridBagConstraints.HORIZONTAL; 289 | panel.add(inputOnDeviceButton, c); 290 | 291 | inputOnDeviceButton.addActionListener(e -> { 292 | final String text2send = Messages.showMultilineInputDialog(project, resourceBundle.getString("send_text.message"), resourceBundle.getString("send_text.title"), storage.getLastSentText(), Messages.getQuestionIcon(), null); 293 | 294 | if (text2send != null) { 295 | storage.setLastSentText(text2send); 296 | 297 | ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { 298 | ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); 299 | userAction = true; 300 | doInputOnDevice(text2send); 301 | userAction = false; 302 | }, resourceBundle.getString("processing.title"), false, null); 303 | } 304 | }); 305 | 306 | clearDataButton = new JButton(resourceBundle.getString("button.clear_data")); 307 | c.gridx = 0; 308 | c.gridy = 5; 309 | c.gridwidth = 2; 310 | c.fill = GridBagConstraints.HORIZONTAL; 311 | panel.add(clearDataButton, c); 312 | clearDataButton.addActionListener(actionEvent -> { 313 | ArrayList appIds = new ArrayList(); 314 | List androidFacets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID); 315 | if (androidFacets != null) { 316 | for (AndroidFacet facet : androidFacets) { 317 | AndroidFacetConfiguration facetConfig = facet.getConfiguration(); 318 | if (!facetConfig.isLibraryProject()) { 319 | String appId = facetConfig.getModel().getApplicationId(); 320 | appIds.add(appId); 321 | } 322 | } 323 | } 324 | 325 | ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { 326 | ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); 327 | userAction = true; 328 | for (String appId : appIds) { 329 | executeShellCommand("pm clear " + appId, false); 330 | } 331 | userAction = false; 332 | }, resourceBundle.getString("processing.title"), false, null); 333 | }); 334 | 335 | killProcessButton = new JButton(resourceBundle.getString("button.kill_process")); 336 | c.gridx = 0; 337 | c.gridy = 6; 338 | c.gridwidth = 2; 339 | c.fill = GridBagConstraints.HORIZONTAL; 340 | panel.add(killProcessButton, c); 341 | killProcessButton.addActionListener(actionEvent -> { 342 | ArrayList appIds = new ArrayList(); 343 | List androidFacets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID); 344 | if (androidFacets != null) { 345 | for (AndroidFacet facet : androidFacets) { 346 | AndroidFacetConfiguration facetConfig = facet.getConfiguration(); 347 | if (!facetConfig.isLibraryProject()) { 348 | AndroidModel androidModel = facetConfig.getModel(); 349 | if (androidModel != null) { 350 | String appId = androidModel.getApplicationId(); 351 | appIds.add(appId); 352 | } 353 | } 354 | } 355 | } 356 | 357 | ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { 358 | ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); 359 | userAction = true; 360 | for (String appId : appIds) { 361 | String res = executeShellCommand("run-as "+appId+" ps -A", false); 362 | if(res!=null) { 363 | LineNumberReader lnr = new LineNumberReader(new StringReader(res)); 364 | try { 365 | String pid = null; 366 | String line = lnr.readLine(); 367 | while(line!=null){ 368 | line = lnr.readLine(); 369 | if(line!=null){ 370 | if(line.contains(appId)){ 371 | StringTokenizer toker = new StringTokenizer(line, " \t"); 372 | if(toker.hasMoreTokens()){ 373 | toker.nextToken(); 374 | if(toker.hasMoreTokens()) { 375 | pid = toker.nextToken(); 376 | break; 377 | } 378 | } 379 | } 380 | } 381 | } 382 | 383 | if(pid!=null){ 384 | res = executeShellCommand("run-as "+appId+" kill "+pid, false); 385 | } 386 | } catch (IOException e) { 387 | e.printStackTrace(); 388 | } 389 | } 390 | } 391 | userAction = false; 392 | }, resourceBundle.getString("processing.title"), false, null); 393 | }); 394 | 395 | JPanel framePanel = new JPanel(new BorderLayout()); 396 | framePanel.add(panel, BorderLayout.NORTH); 397 | return framePanel; 398 | } 399 | 400 | private void doInputOnDevice(String text2send) { 401 | /* 402 | Syntax: 403 | `tap 130 150` -> sends "input tap 130 150" .... really just "input " and append the command 404 | `tap @my.package:id/text` -> first find the center of the given res-id (uiautomator dump /dev/tty) 405 | `tap @*:id/text` -> support wildcards 406 | `swipe 10 20 30 40` -> simple swipe (px) 407 | `swipe @*:id/my_id[10,20] @*:id/my_id[50,60]` -> swipe from 10% of x of given view, 20% of y of given view to 50% of x of the view to 60% of y of the view 408 | `tap @*:id/button[20,30]` syntax also works in general for @id things 409 | ``` -> escapes ` 410 | ` 411 | ` -> new line which is not sent 412 | `#500` -> wait 500 milliseconds 413 | */ 414 | 415 | text2send = text2send.replace("```", "\u2764"); 416 | text2send = text2send + "`"; 417 | StringTokenizer tokenizer = new StringTokenizer(text2send, "`", true); 418 | boolean inCommand = false; 419 | String plainText = null; 420 | String commandText = null; 421 | while (tokenizer.hasMoreElements()) { 422 | String next = tokenizer.nextToken(); 423 | 424 | if ("`".equals(next)) { 425 | if (!inCommand) { 426 | inCommand = true; 427 | 428 | if (plainText != null) { 429 | 430 | StringTokenizer tokenizer2 = new StringTokenizer(plainText," \t\r\n", true); 431 | while(tokenizer2.hasMoreElements()) { 432 | String part = tokenizer2.nextToken(); 433 | String escaped = part.replace("\"", "\\\"").replace("\u2764", "\\`"); 434 | executeShellCommand("input text \"" + escaped + "\"", false); 435 | 436 | try { 437 | Thread.sleep(400); // wait a bit - give the device some time to process 438 | } catch (InterruptedException e) { 439 | //ignored 440 | } 441 | } 442 | plainText = null; 443 | commandText = null; 444 | } 445 | } else { 446 | inCommand = false; 447 | if (commandText != null) { 448 | commandText = commandText.replace("\r", "").replace("\n", ""); 449 | if (commandText.length() > 0) { 450 | if (commandText.startsWith("#")) { 451 | long timeToWait = Long.parseLong(commandText.substring(1)); 452 | try { 453 | Thread.sleep(timeToWait); 454 | } catch (InterruptedException e) { 455 | // do nothing 456 | } 457 | } else { 458 | if (commandText.contains("@")) { 459 | commandText = processViewIds(commandText); 460 | } 461 | 462 | executeShellCommand("input " + commandText, false); 463 | } 464 | } 465 | commandText = null; 466 | plainText = null; 467 | } 468 | } 469 | } else { 470 | if (inCommand) { 471 | commandText = next; 472 | plainText = null; 473 | } else { 474 | plainText = next; 475 | commandText = null; 476 | } 477 | } 478 | } 479 | 480 | try { 481 | Thread.sleep(800); // wait a bit - give the device some time to process 482 | } catch (InterruptedException e) { 483 | //ignored 484 | } 485 | } 486 | 487 | private String processViewIds(String commandText) { 488 | /* 489 | @ defaults to center of view 490 | @[percentX,percentY] percentX/Y in view bounds 491 | */ 492 | String views = executeShellCommand("uiautomator dump /dev/tty", false); 493 | views = views.substring(0, views.lastIndexOf(">") + 1); 494 | HashMap resIdToBoundsMap = new HashMap<>(); 495 | try { 496 | XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); 497 | xpp.setInput(new StringReader(views)); 498 | 499 | int eventType; 500 | while ((eventType = xpp.getEventType()) != XmlPullParser.END_DOCUMENT) { 501 | if (eventType == XmlPullParser.START_TAG) { 502 | if ("node".equals(xpp.getName())) { 503 | String bounds = XmlPullUtil.getAttributeValue(xpp, "bounds"); 504 | String resId = XmlPullUtil.getAttributeValue(xpp, "resource-id"); 505 | 506 | if (resId != null && resId.length() > 0) { 507 | bounds = bounds.replace("][", ","); 508 | bounds = bounds.replace("[", ""); 509 | bounds = bounds.replace("]", ""); 510 | String[] coords = bounds.split(","); 511 | int x1 = Integer.parseInt(coords[0]); 512 | int y1 = Integer.parseInt(coords[1]); 513 | int x2 = Integer.parseInt(coords[2]); 514 | int y2 = Integer.parseInt(coords[3]); 515 | 516 | Rectangle rect = new Rectangle(x1, y1, x2 - x1, y2 - y1); 517 | resIdToBoundsMap.put(resId, rect); 518 | } 519 | } 520 | } 521 | xpp.next(); 522 | 523 | } 524 | 525 | } catch (XmlPullParserException | IOException e) { 526 | e.printStackTrace(); 527 | } 528 | 529 | while (commandText.contains("@")) { 530 | int idx = commandText.indexOf("@"); 531 | StringBuilder sb = new StringBuilder(); 532 | int i = idx; 533 | while (i < commandText.length() && commandText.charAt(i) > ' ') { 534 | sb.append(commandText.charAt(i)); 535 | i++; 536 | } 537 | 538 | String calculatedCoords = "0 0"; 539 | String resIdToMatch = sb.substring(1); 540 | double percentX = 0.5; 541 | double percentY = 0.5; 542 | 543 | if (resIdToMatch.contains("[") && resIdToMatch.contains("]")) { 544 | String percentPart = resIdToMatch.substring(resIdToMatch.indexOf("[") + 1, resIdToMatch.indexOf("]")); 545 | resIdToMatch = resIdToMatch.substring(0, resIdToMatch.indexOf("[")); 546 | String[] percentParts = percentPart.split(","); 547 | try { 548 | percentX = Double.parseDouble(percentParts[0]) / 100; 549 | percentY = Double.parseDouble(percentParts[1]) / 100; 550 | } catch (NumberFormatException nfe) { 551 | nfe.printStackTrace(); 552 | } 553 | } 554 | 555 | Rectangle rect = new Rectangle(0, 0, 0, 0); 556 | if (resIdToBoundsMap.containsKey(resIdToMatch)) { 557 | rect = resIdToBoundsMap.get(resIdToMatch); 558 | } else { 559 | if (!resIdToMatch.contains(":")) { 560 | resIdToMatch = "*:" + resIdToMatch; 561 | } 562 | 563 | for (Map.Entry entry : resIdToBoundsMap.entrySet()) { 564 | if (wildcardMatch(resIdToMatch, entry.getKey())) { 565 | rect = entry.getValue(); 566 | break; 567 | } 568 | } 569 | } 570 | calculatedCoords = "" + (int) (rect.x + rect.width * percentX) + " " + (int) (rect.y + rect.height * percentY); 571 | 572 | commandText = commandText.substring(0, idx) + calculatedCoords + commandText.substring(idx + sb.length()); 573 | } 574 | 575 | return commandText; 576 | } 577 | 578 | public static boolean wildcardMatch(final String toMatch, final String value) { 579 | StringBuilder patternStringBuilder = new StringBuilder(); 580 | for (final char c : toMatch.toCharArray()) { 581 | switch (c) { 582 | case '?': 583 | patternStringBuilder.append(".?"); 584 | break; 585 | case '*': 586 | patternStringBuilder.append(".*"); 587 | break; 588 | default: 589 | patternStringBuilder.append(Pattern.quote(String.valueOf(c))); 590 | break; 591 | } 592 | } 593 | Pattern pattern = Pattern.compile(patternStringBuilder.toString()); 594 | return pattern.matcher(value).matches(); 595 | } 596 | 597 | private void updateFromDevice() { 598 | ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { 599 | ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); 600 | IDevice selectedDevice = getSelectedDevice(); 601 | if (selectedDevice != null) { 602 | 603 | setupDevice(selectedDevice); 604 | 605 | String debugLayoutProperty = getSysPropFromDevice("debug.layout", selectedDevice); 606 | if ("true".equals(debugLayoutProperty)) { 607 | showLayoutBounds.setSelected(true); 608 | } else { 609 | showLayoutBounds.setSelected(false); 610 | } 611 | 612 | String deviceLocale = getSysPropFromDevice("persist.sys.locale", selectedDevice); 613 | int i = 0; 614 | for (LocaleData ld : LOCALES) { 615 | if (deviceLocale != null && deviceLocale.startsWith(ld.language) && deviceLocale.endsWith(ld.county)) { 616 | final int toSelect = i; 617 | SwingUtilities.invokeLater(() -> localeChooser.setSelectedIndex(toSelect)); 618 | 619 | break; 620 | } 621 | i++; 622 | } 623 | 624 | SwingUtilities.invokeLater(() -> enableAll()); 625 | } else { 626 | SwingUtilities.invokeLater(() -> disableAll()); 627 | } 628 | }, resourceBundle.getString("initializing.device.message"), false, null); 629 | } 630 | 631 | private String getSysPropFromDevice(String propName, IDevice selectedDevice) { 632 | try { 633 | return selectedDevice.getSystemProperty(propName).get(); 634 | } catch (InterruptedException | ExecutionException e) { 635 | e.printStackTrace(); 636 | } 637 | return null; 638 | } 639 | 640 | private void disableAll() { 641 | showLayoutBounds.setEnabled(false); 642 | localeChooser.setEnabled(false); 643 | goToActivityButton.setEnabled(false); 644 | inputOnDeviceButton.setEnabled(false); 645 | clearDataButton.setEnabled(false); 646 | killProcessButton.setEnabled(false); 647 | } 648 | 649 | private void enableAll() { 650 | showLayoutBounds.setEnabled(true); 651 | localeChooser.setEnabled(true); 652 | goToActivityButton.setEnabled(true); 653 | inputOnDeviceButton.setEnabled(true); 654 | clearDataButton.setEnabled(true); 655 | killProcessButton.setEnabled(true); 656 | } 657 | 658 | private void setupDevice(final IDevice selectedDevice) { 659 | try { 660 | installEnablerApk(selectedDevice); 661 | } catch (IOException e) { 662 | e.printStackTrace(); 663 | } 664 | } 665 | 666 | @SuppressWarnings("unchecked") 667 | private void updateDeviceComboBox() { 668 | devices.removeActionListener(deviceSelectedListener); 669 | String selectedDevice = (String) devices.getSelectedItem(); 670 | 671 | IDevice[] devs = adBridge.getDevices(); 672 | Vector devicesList = new Vector(); 673 | devicesList.add("-- none --"); 674 | for (IDevice device : devs) { 675 | devicesList.add(device.toString()); 676 | } 677 | devices.setModel(new DefaultComboBoxModel<>(devicesList)); 678 | 679 | if (devicesList.size() == 1) { 680 | disableAll(); 681 | } else { 682 | devices.setSelectedItem(selectedDevice); 683 | 684 | devices.setSelectedItem(devices.getSelectedItem()); 685 | if (devices.getSelectedIndex() == 0) { 686 | disableAll(); 687 | } else { 688 | enableAll(); 689 | } 690 | } 691 | 692 | devices.addActionListener(deviceSelectedListener); 693 | } 694 | 695 | private String executeShellCommand(String cmd, boolean doPoke) { 696 | if (!userAction) { 697 | return null; 698 | } 699 | 700 | if (devices.getSelectedIndex() == 0) { 701 | return null; 702 | } 703 | 704 | String res = null; 705 | String selDevice = (String) devices.getSelectedItem(); 706 | for (IDevice device : adBridge.getDevices()) { 707 | if (selDevice.equals(device.toString())) { 708 | 709 | try { 710 | rcv.reset(); 711 | device.executeShellCommand(cmd, rcv); 712 | res = rcv.getResult(); 713 | if (doPoke) { 714 | device.executeShellCommand("am start -a POKESYSPROPS", rcv); 715 | } 716 | } catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException | IOException e1) { 717 | e1.printStackTrace(); 718 | } 719 | 720 | } 721 | } 722 | return res; 723 | } 724 | 725 | private void installEnablerApk(IDevice device) throws IOException { 726 | String serial = "<"+device.getSerialNumber()+">"; 727 | String alreadyInstalledOn = storage.getInstalledOnDevices(); 728 | if(alreadyInstalledOn==null){ 729 | alreadyInstalledOn = ""; 730 | } 731 | if(alreadyInstalledOn.contains(serial)){ 732 | return; 733 | } 734 | storage.setInstalledOnDevices(alreadyInstalledOn+serial); 735 | 736 | // TODO no need to create the tmp file over and over again 737 | File tmpfile = File.createTempFile("enabler", "apk"); 738 | FileOutputStream fos = null; 739 | InputStream is = null; 740 | try { 741 | is = getClass().getResourceAsStream("/de/mobilej/plugin/adc/enabler.apk"); 742 | fos = new FileOutputStream(tmpfile); 743 | byte[] buffer = new byte[4096]; 744 | int len = 0; 745 | while ((len = is.read(buffer)) > 0) { 746 | fos.write(buffer, 0, len); 747 | } 748 | } finally { 749 | if (fos != null) { 750 | fos.flush(); 751 | fos.close(); 752 | } 753 | if (is != null) { 754 | is.close(); 755 | } 756 | } 757 | 758 | try { 759 | device.installPackage(tmpfile.getAbsolutePath(), true); 760 | } catch (InstallException ie) { 761 | ie.printStackTrace(); 762 | } 763 | 764 | try { 765 | device.executeShellCommand("pm grant mobilej.de.systemproppoker android.permission.CHANGE_CONFIGURATION", rcv); 766 | } catch (Exception e) { 767 | e.printStackTrace(); 768 | } 769 | } 770 | 771 | private IDevice getSelectedDevice() { 772 | String selDevice = (String) devices.getSelectedItem(); 773 | for (IDevice device : adBridge.getDevices()) { 774 | if (selDevice.equals(device.toString())) { 775 | return device; 776 | } 777 | } 778 | return null; 779 | } 780 | 781 | /** 782 | * Holder for Locale Data 783 | */ 784 | private static class LocaleData { 785 | final String name; 786 | final String language; 787 | final String county; 788 | 789 | LocaleData(String name, String language, String county) { 790 | this.name = name; 791 | this.language = language; 792 | this.county = county; 793 | } 794 | 795 | @Override 796 | public String toString() { 797 | return name; 798 | } 799 | } 800 | 801 | } --------------------------------------------------------------------------------