diff --git a/build.gradle b/build.gradle index 4ad1bba..eb0ad60 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ buildscript { - ext.kotlin_version = '1.2.50' + ext.kotlin_version = '1.3.41' repositories { + maven {url 'https://maven.aliyun.com/repository/public'} mavenCentral() jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "net.ltgt.gradle:gradle-apt-plugin:0.9" } } @@ -27,6 +27,7 @@ apply plugin: 'kotlin-kapt' repositories { + maven {url 'https://maven.aliyun.com/repository/public'} mavenCentral() jcenter() } @@ -53,22 +54,21 @@ intellij { group 'com.developerphil.intellij.plugin.adbidea' dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" - implementation "org.jetbrains.kotlin:kotlin-runtime:${kotlin_version}" + compile "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" + compile "org.jooq:joor-java-8:0.9.7" compileOnly fileTree(dir: "$StudioCompilePath/plugins/android/lib", include: ['*.jar']) compileOnly fileTree(dir: "$StudioCompilePath/lib", include: ['*.jar']) - implementation 'com.google.dagger:dagger:2.6' - kapt "com.google.dagger:dagger-compiler:2.6" testImplementation 'junit:junit:4.12' testImplementation fileTree(dir: "$StudioCompilePath/plugins/android/lib", include: ['*.jar']) testImplementation fileTree(dir: "$StudioCompilePath/lib", include: ['*.jar']) testImplementation "org.mockito:mockito-core:1.+" - testImplementation "com.google.truth:truth:0.30" + testImplementation "com.google.truth:truth:1.0.1" + } -task verifySetup() { +task(verifySetup) { doLast { def ideaJar = "$StudioCompilePath/lib/idea.jar" if (!file(ideaJar).exists()) { @@ -78,4 +78,4 @@ task verifySetup() { } -compileJava.dependsOn verifySetup +compileJava.dependsOn verifySetup \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 6ffa237..f3d88b1 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48f75ae..a95009c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Dec 03 09:34:18 EST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip diff --git a/gradlew b/gradlew index 9aa616c..2fe81a7 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -28,16 +44,16 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` @@ -138,32 +154,30 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=`save "$@"` -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then - cd "$(dirname "$0")" -fi +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d..24467a1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/src/main/java/com/developerphil/adbidea/action/AdbAction.java b/src/main/java/com/developerphil/adbidea/action/AdbAction.java deleted file mode 100644 index c19f1d0..0000000 --- a/src/main/java/com/developerphil/adbidea/action/AdbAction.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.project.Project; - -public abstract class AdbAction extends AnAction { - - - @Override - public final void actionPerformed(AnActionEvent e) { - final Project project = e.getData(PlatformDataKeys.PROJECT); - actionPerformed(e, project); - } - - public abstract void actionPerformed(AnActionEvent e, Project project); -} diff --git a/src/main/java/com/developerphil/adbidea/action/ClearDataAction.java b/src/main/java/com/developerphil/adbidea/action/ClearDataAction.java deleted file mode 100644 index e4cd94b..0000000 --- a/src/main/java/com/developerphil/adbidea/action/ClearDataAction.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class ClearDataAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.clearData(project); - } - - -} diff --git a/src/main/java/com/developerphil/adbidea/action/ClearDataAndRestartAction.java b/src/main/java/com/developerphil/adbidea/action/ClearDataAndRestartAction.java deleted file mode 100644 index 6a3816a..0000000 --- a/src/main/java/com/developerphil/adbidea/action/ClearDataAndRestartAction.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class ClearDataAndRestartAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.clearDataAndRestart(project); - } - - -} diff --git a/src/main/java/com/developerphil/adbidea/action/GrantPermissionsAction.java b/src/main/java/com/developerphil/adbidea/action/GrantPermissionsAction.java deleted file mode 100644 index 9796514..0000000 --- a/src/main/java/com/developerphil/adbidea/action/GrantPermissionsAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class GrantPermissionsAction extends AdbAction { - @Override - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.grantPermissions(project); - } -} diff --git a/src/main/java/com/developerphil/adbidea/action/KillAction.java b/src/main/java/com/developerphil/adbidea/action/KillAction.java deleted file mode 100644 index c233e38..0000000 --- a/src/main/java/com/developerphil/adbidea/action/KillAction.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class KillAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.kill(project); - } - -} diff --git a/src/main/java/com/developerphil/adbidea/action/QuickListAction.java b/src/main/java/com/developerphil/adbidea/action/QuickListAction.java deleted file mode 100644 index 94349d3..0000000 --- a/src/main/java/com/developerphil/adbidea/action/QuickListAction.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.intellij.ide.actions.QuickSwitchSchemeAction; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static com.developerphil.adbidea.adb.AdbUtil.isDebuggingAvailable; - -public class QuickListAction extends QuickSwitchSchemeAction implements DumbAware { - @Override - protected void fillActions(@Nullable final Project project, - @NotNull final DefaultActionGroup group, - @NotNull final DataContext dataContext) { - - if (project == null) { - return; - } - - addAction("com.developerphil.adbidea.action.UninstallAction", group); - addAction("com.developerphil.adbidea.action.KillAction", group); - addAction("com.developerphil.adbidea.action.StartAction", group); - addAction("com.developerphil.adbidea.action.RestartAction", group); - addAction("com.developerphil.adbidea.action.ClearDataAction", group); - addAction("com.developerphil.adbidea.action.ClearDataAndRestartAction", group); - addAction("com.developerphil.adbidea.action.RevokePermissionsAction", group); - - if (isDebuggingAvailable()) { - group.addSeparator(); - - addAction("com.developerphil.adbidea.action.StartWithDebuggerAction", group); - addAction("com.developerphil.adbidea.action.RestartWithDebuggerAction", group); - } - group.addSeparator(); - addAction("com.developerphil.adbidea.action.QuickListSupplementaryAction", group); - - } - - protected boolean isEnabled() { - return true; - } - - private void addAction(final String actionId, final DefaultActionGroup toGroup) { - final AnAction action = ActionManager.getInstance().getAction(actionId); - - // add action to group if it is available - if (action != null) { - toGroup.add(action); - } - } - - protected String getPopupTitle(AnActionEvent e) { - return "ADB Operations Popup"; - } - -} diff --git a/src/main/java/com/developerphil/adbidea/action/QuickListSupplementaryAction.java b/src/main/java/com/developerphil/adbidea/action/QuickListSupplementaryAction.java deleted file mode 100644 index 0b9e3cb..0000000 --- a/src/main/java/com/developerphil/adbidea/action/QuickListSupplementaryAction.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.intellij.ide.actions.QuickSwitchSchemeAction; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class QuickListSupplementaryAction extends QuickSwitchSchemeAction implements DumbAware { - @Override - protected void fillActions(@Nullable final Project project, - @NotNull final DefaultActionGroup group, - @NotNull final DataContext dataContext) { - - if (project == null) { - return; - } - addAction("com.developerphil.adbidea.action.extend.ApplicationManagementPopupAction", group); - addAction("com.developerphil.adbidea.action.extend.InteractingAction", group); - addAction("com.developerphil.adbidea.action.extend.ShowDeviceInfoAction", group); - addAction("com.developerphil.adbidea.action.extend.InstallApkAction", group); - addAction("com.developerphil.adbidea.action.extend.PutStringAction", group); - addAction("com.developerphil.adbidea.action.extend.ScreenRecordAction", group); - addAction("com.developerphil.adbidea.action.extend.ScreenCaptureAction", group); - } - - protected boolean isEnabled() { - return true; - } - - private void addAction(final String actionId, final DefaultActionGroup toGroup) { - final AnAction action = ActionManager.getInstance().getAction(actionId); - - // add action to group if it is available - if (action != null) { - toGroup.add(action); - } - } - - protected String getPopupTitle(AnActionEvent e) { - return "ADB Supplementary Operations Popup"; - } - -} diff --git a/src/main/java/com/developerphil/adbidea/action/RestartAction.java b/src/main/java/com/developerphil/adbidea/action/RestartAction.java deleted file mode 100644 index baf7229..0000000 --- a/src/main/java/com/developerphil/adbidea/action/RestartAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class RestartAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.restartDefaultActivity(project); - } -} diff --git a/src/main/java/com/developerphil/adbidea/action/RestartWithDebuggerAction.java b/src/main/java/com/developerphil/adbidea/action/RestartWithDebuggerAction.java deleted file mode 100644 index 605c5fe..0000000 --- a/src/main/java/com/developerphil/adbidea/action/RestartWithDebuggerAction.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -import static com.developerphil.adbidea.adb.AdbUtil.isDebuggingAvailable; - -public class RestartWithDebuggerAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.restartDefaultActivityWithDebugger(project); - } - - @Override - public void update(AnActionEvent e) { - e.getPresentation().setEnabled(isDebuggingAvailable()); - } - -} diff --git a/src/main/java/com/developerphil/adbidea/action/RevokePermissionsAction.java b/src/main/java/com/developerphil/adbidea/action/RevokePermissionsAction.java deleted file mode 100644 index aad54a0..0000000 --- a/src/main/java/com/developerphil/adbidea/action/RevokePermissionsAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class RevokePermissionsAction extends AdbAction { - @Override - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.revokePermissions(project); - } -} diff --git a/src/main/java/com/developerphil/adbidea/action/RevokePermissionsAndRestartAction.java b/src/main/java/com/developerphil/adbidea/action/RevokePermissionsAndRestartAction.java deleted file mode 100644 index b5f1a48..0000000 --- a/src/main/java/com/developerphil/adbidea/action/RevokePermissionsAndRestartAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class RevokePermissionsAndRestartAction extends AdbAction { - @Override - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.revokePermissionsAndRestart(project); - } -} diff --git a/src/main/java/com/developerphil/adbidea/action/StartAction.java b/src/main/java/com/developerphil/adbidea/action/StartAction.java deleted file mode 100644 index 8136cbb..0000000 --- a/src/main/java/com/developerphil/adbidea/action/StartAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class StartAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.startDefaultActivity(project); - } -} diff --git a/src/main/java/com/developerphil/adbidea/action/StartWithDebuggerAction.java b/src/main/java/com/developerphil/adbidea/action/StartWithDebuggerAction.java deleted file mode 100644 index f146884..0000000 --- a/src/main/java/com/developerphil/adbidea/action/StartWithDebuggerAction.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -import static com.developerphil.adbidea.adb.AdbUtil.isDebuggingAvailable; - -public class StartWithDebuggerAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.startDefaultActivityWithDebugger(project); - } - - @Override - public void update(AnActionEvent e) { - e.getPresentation().setEnabled(isDebuggingAvailable()); - } -} diff --git a/src/main/java/com/developerphil/adbidea/action/UninstallAction.java b/src/main/java/com/developerphil/adbidea/action/UninstallAction.java deleted file mode 100644 index 75bfb0c..0000000 --- a/src/main/java/com/developerphil/adbidea/action/UninstallAction.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.developerphil.adbidea.action; - -import com.developerphil.adbidea.adb.AdbFacade; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; - -public class UninstallAction extends AdbAction { - - public void actionPerformed(AnActionEvent e, Project project) { - AdbFacade.uninstall(project); - } - - -} diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java deleted file mode 100644 index 480dc70..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.developerphil.adbidea.adb; - -import com.android.ddmlib.IDevice; -import com.developerphil.adbidea.ObjectGraph; -import com.developerphil.adbidea.adb.command.ActivityServiceCommand; -import com.developerphil.adbidea.adb.command.CaptureScreenCommand; -import com.developerphil.adbidea.adb.command.ClearDataAndRestartCommand; -import com.developerphil.adbidea.adb.command.ClearDataCommand; -import com.developerphil.adbidea.adb.command.Command; -import com.developerphil.adbidea.adb.command.CommandList; -import com.developerphil.adbidea.adb.command.CommonStringResultCommand; -import com.developerphil.adbidea.adb.command.ForceStopCommand; -import com.developerphil.adbidea.adb.command.ForegroundActivityCommand; -import com.developerphil.adbidea.adb.command.GetApplicationListCommand; -import com.developerphil.adbidea.adb.command.GrantPermissionsCommand; -import com.developerphil.adbidea.adb.command.InstallApkCommand; -import com.developerphil.adbidea.adb.command.InteractingCommandKt; -import com.developerphil.adbidea.adb.command.KillCommand; -import com.developerphil.adbidea.adb.command.MonkeyTestCommand; -import com.developerphil.adbidea.adb.command.PackageDetailCommand; -import com.developerphil.adbidea.adb.command.PackagePathCommand; -import com.developerphil.adbidea.adb.command.PullFileCommand; -import com.developerphil.adbidea.adb.command.PutStringToDeviceCommand; -import com.developerphil.adbidea.adb.command.RestartPackageCommand; -import com.developerphil.adbidea.adb.command.RevokePermissionsAndRestartCommand; -import com.developerphil.adbidea.adb.command.RevokePermissionsCommand; -import com.developerphil.adbidea.adb.command.ScreenRecordCommand; -import com.developerphil.adbidea.adb.command.StartDefaultActivityCommand; -import com.developerphil.adbidea.adb.command.UninstallCommand; -import com.developerphil.adbidea.bean.BoundItemBean; -import com.developerphil.adbidea.ui.NotificationHelper; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import com.intellij.openapi.project.Project; -import java.io.File; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import static com.developerphil.adbidea.adb.AdbUtil.isGradleSyncInProgress; -import static com.developerphil.adbidea.ui.NotificationHelper.error; - -public class AdbFacade { - - private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("AdbIdea-%d").build()); - - public static void uninstall(Project project, String packageName) { - executeOnDevice(project, new UninstallCommand(packageName)); - } - - public static void uninstall(Project project) { - executeOnDevice(project, new UninstallCommand()); - } - - public static void installApk(Project project, List apks) { - executeOnDevice(project, new InstallApkCommand(apks)); - } - - public static void kill(Project project) { - executeOnDevice(project, new KillCommand()); - } - - public static void grantPermissions(Project project) { - executeOnDevice(project, new GrantPermissionsCommand()); - } - - public static void revokePermissions(Project project) { - executeOnDevice(project, new RevokePermissionsCommand()); - } - - public static void revokePermissionsAndRestart(Project project) { - executeOnDevice(project, new RevokePermissionsAndRestartCommand()); - } - - public static void startDefaultActivity(Project project) { - executeOnDevice(project, new StartDefaultActivityCommand(false)); - } - - public static void startDefaultActivityWithDebugger(Project project) { - executeOnDevice(project, new StartDefaultActivityCommand(true)); - } - - public static void restartDefaultActivity(Project project) { - executeOnDevice(project, new RestartPackageCommand()); - } - - public static void restartDefaultActivityWithDebugger(Project project) { - executeOnDevice(project, new CommandList(new KillCommand(), new StartDefaultActivityCommand(true))); - } - - public static void clearData(Project project) { - executeOnDevice(project, new ClearDataCommand()); - } - - public static void getPackageDetail(Project project, String packageName, Function1 callback) { - executeOnDevice(project, new PackageDetailCommand(packageName, callback)); - } - - public static void forceStop(Project project, String packageName) { - executeOnDevice(project, new ForceStopCommand(packageName)); - } - - public static void getPackagePath(Project project, String packageName, Function1 callback) { - executeOnDevice(project, new PackagePathCommand(packageName, callback)); - } - - public static void getActivityService(Project project, String packageName, Function1 callback) { - executeOnDevice(project, new ActivityServiceCommand(packageName, callback)); - } - - public static void clearDataAndRestart(Project project) { - executeOnDevice(project, new ClearDataAndRestartCommand()); - } - - public static void getAllApplicationList(Project project, String parameter, Function1, Unit> callback) { - executeOnDevice(project, new GetApplicationListCommand(parameter, callback)); - } - - private static void executeOnDevice(final Project project, final Command runnable) { - - if (isGradleSyncInProgress(project)) { - NotificationHelper.error("Gradle sync is in progress"); - return; - } - - final DeviceResult result = project.getComponent(ObjectGraph.class).getDeviceResultFetcher().fetch(); - - if (result != null) { - for (final IDevice device : result.getDevices()) { - EXECUTOR.submit((Runnable) () -> runnable.run(project, device, result.getFacet(), result.getPackageName())); - } - } else { - error("No Device found"); - } - } - - public static void clearData(Project project, String realPackageName) { - executeOnDevice(project, new ClearDataCommand(realPackageName)); - } - - public static void showForegroundActivity(Project project, Function1 callback) { - executeOnDevice(project, new ForegroundActivityCommand(callback)); - } - - public static void monkeyTest(Project project,String packageName,int count ,Function1 callback) { - executeOnDevice(project, new MonkeyTestCommand(packageName,count,callback)); - } - - public static void putStringToDevice(@Nullable Project project, @NotNull String str) { - executeOnDevice(project, new PutStringToDeviceCommand(str)); - } - - public static void interacting(Project project, int type, String action, String category, String name, List boundData) { - executeOnDevice(project, InteractingCommandKt.getInteractingCommand(type, action, category, name, boundData)); - } - - public static void getSimpleInfo(Project project, String command, String desc, Function1 callback) { - executeOnDevice(project, new CommonStringResultCommand(command, desc, callback)); - } - - public static void captureScreen(@Nullable Project project, File localDir, String fileName) { - executeOnDevice(project, new CaptureScreenCommand(localDir,fileName)); - } - - /** - * "bad result" - * @param project - * @param localFile - * @param videoName - * @param length - * @param showTouches - */ - @Deprecated() - public static void recordScreen(@Nullable Project project, File localFile, String videoName, int length, boolean showTouches) { - executeOnDevice(project, new ScreenRecordCommand(localFile, videoName, length, showTouches)); - } - - public static void pullFile(@Nullable Project project, @NotNull String remotePath, @NotNull File localFile, boolean deleteRemoteFile) { - executeOnDevice(project, new PullFileCommand(remotePath, localFile, deleteRemoteFile)); - } - - public static void getDeviceModel(@Nullable Project project, @NotNull Function1 function) { - getSimpleInfo(project, "getprop ro.product.model", "get Device model ", s -> { - function.invoke(s.replace("\n", "").replace("\r", "").replace(" ", "")); - return null; - }); - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbUtil.java b/src/main/java/com/developerphil/adbidea/adb/AdbUtil.java deleted file mode 100644 index 77878ba..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/AdbUtil.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.developerphil.adbidea.adb; - -import com.android.ddmlib.AdbCommandRejectedException; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.Log; -import com.android.ddmlib.ShellCommandUnresponsiveException; -import com.android.ddmlib.SyncException; -import com.android.ddmlib.SyncService; -import com.android.ddmlib.TimeoutException; -import com.android.tools.idea.gradle.project.sync.GradleSyncState; -import com.developerphil.adbidea.adb.command.receiver.GenericReceiver; -import com.developerphil.adbidea.ui.NotificationHelper; -import com.intellij.openapi.project.Project; -import java.io.File; -import java.io.IOException; -import java.util.concurrent.TimeUnit; -import org.joor.Reflect; - -public class AdbUtil { - - public static boolean isAppInstalled(IDevice device, String packageName) throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException { - GenericReceiver receiver = new GenericReceiver(); - // "pm list packages com.my.package" will return one line per package installed that corresponds to this package. - // if this list is empty, we know for sure that the app is not installed - device.executeShellCommand("pm list packages " + packageName, receiver, 15L, TimeUnit.SECONDS); - - //TODO make sure that it is the exact package name and not a subset. - // e.g. if our app is called com.example but there is another app called com.example.another.app, it will match and return a false positive - return !receiver.getAdbOutputLines().isEmpty(); - } - - // The android debugger class is not available in Intellij 2016.1. - // Nobody should use that version but it's still the minimum "supported" version since android studio 2.2 - // shares the same base version. - public static Boolean isDebuggingAvailable() { - try { - Reflect.on("com.android.tools.idea.run.editor.AndroidDebugger").get(); - return true; - } catch (Exception e) { - return false; - } - } - - public static boolean isGradleSyncInProgress(Project project) { - try { - return GradleSyncState.getInstance(project).isSyncInProgress(); - } catch (Throwable t) { - NotificationHelper.info("Couldn't determine if a gradle sync is in progress"); - return false; - } - } - - - public static void pullFile(IDevice device,String remote, String local,SyncService.ISyncProgressMonitor monitor) throws IOException, AdbCommandRejectedException, com.android.ddmlib.TimeoutException, SyncException { - SyncService sync = null; - try { - String targetFileName =(new File(remote)).getName(); - Log.d(targetFileName, String.format("Downloading %1$s from device '%2$s'", targetFileName, device.getSerialNumber())); - sync = device.getSyncService(); - if (sync == null) { - throw new IOException("Unable to open sync connection!"); - } - String message = String.format("Downloading file from device '%1$s'", device.getSerialNumber()); - Log.d("Device", message); - sync.pullFile(remote, local, monitor); - } catch (com.android.ddmlib.TimeoutException var11) { - Log.e("Device", "Error during Sync: timeout."); - throw var11; - } catch (SyncException var12) { - Log.e("Device", String.format("Error during Sync: %1$s", var12.getMessage())); - throw var12; - } catch (IOException var13) { - Log.e("Device", String.format("Error during Sync: %1$s", var13.getMessage())); - throw var13; - } finally { - if (sync != null) { - sync.close(); - } - } - } - -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/ClearDataAndRestartCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/ClearDataAndRestartCommand.java deleted file mode 100644 index 3d341bb..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/ClearDataAndRestartCommand.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -public class ClearDataAndRestartCommand extends CommandList { - public ClearDataAndRestartCommand() { - super(new ClearDataCommand(), new StartDefaultActivityCommand(false)); - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/ClearDataCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/ClearDataCommand.java deleted file mode 100644 index 269c71b..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/ClearDataCommand.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.developerphil.adbidea.adb.command.receiver.GenericReceiver; -import com.intellij.openapi.project.Project; -import org.jetbrains.android.facet.AndroidFacet; - -import java.util.concurrent.TimeUnit; - -import static com.developerphil.adbidea.adb.AdbUtil.isAppInstalled; -import static com.developerphil.adbidea.ui.NotificationHelper.error; -import static com.developerphil.adbidea.ui.NotificationHelper.info; - -public class ClearDataCommand implements Command { - - private String mPackageName; - - public ClearDataCommand() { - } - - public ClearDataCommand(String realPackageName) { - mPackageName = realPackageName; - } - - @Override - public boolean run(Project project, IDevice device, AndroidFacet facet, String packageName) { - if (mPackageName != null) { - packageName = mPackageName; - } - try { - if (isAppInstalled(device, packageName)) { - device.executeShellCommand("pm clear " + packageName, new GenericReceiver(), 15L, TimeUnit.SECONDS); - info(String.format("%s cleared data for app on %s", packageName, device.getName())); - return true; - } else { - error(String.format("%s is not installed on %s", packageName, device.getName())); - } - } catch (Exception e1) { - error("Clear data failed... " + e1.getMessage()); - } - - return false; - } - -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/Command.java b/src/main/java/com/developerphil/adbidea/adb/command/Command.java deleted file mode 100644 index 4885915..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/Command.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.intellij.openapi.project.Project; -import org.jetbrains.android.facet.AndroidFacet; - -public interface Command { - /** - * - * @return true if the command executed properly - */ - boolean run(Project project, IDevice device, AndroidFacet facet, String packageName); -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/CommandList.java b/src/main/java/com/developerphil/adbidea/adb/command/CommandList.java deleted file mode 100644 index b5f6055..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/CommandList.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.intellij.openapi.project.Project; -import org.jetbrains.android.facet.AndroidFacet; - -import java.util.ArrayList; -import java.util.List; - -public class CommandList implements Command { - - private List commands; - - public CommandList(Command... commands) { - this.commands = new ArrayList(); - for (Command command : commands) { - this.commands.add(command); - } - } - - @Override - public boolean run(Project project, IDevice device, AndroidFacet facet, String packageName) { - for (Command command : commands) { - boolean success = command.run(project, device, facet, packageName); - if (!success) { - return false; - } - } - - return true; - } - - -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/GrantPermissionsCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/GrantPermissionsCommand.java deleted file mode 100644 index c18353f..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/GrantPermissionsCommand.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.developerphil.adbidea.adb.command.receiver.GenericReceiver; -import com.intellij.openapi.project.Project; -import org.jetbrains.android.facet.AndroidFacet; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static com.developerphil.adbidea.adb.AdbUtil.isAppInstalled; -import static com.developerphil.adbidea.ui.NotificationHelper.error; -import static com.developerphil.adbidea.ui.NotificationHelper.info; - -public class GrantPermissionsCommand implements Command { - @Override - public boolean run(Project project, IDevice device, AndroidFacet facet, String packageName) { - try { - if (deviceHasMarshmallow(device)) - if (isAppInstalled(device, packageName)) { - GenericReceiver shellOutputReceiver = new GenericReceiver(); - device.executeShellCommand("dumpsys package " + packageName, shellOutputReceiver, 15L, TimeUnit.SECONDS); - List adbOutputLines = getRequestedPermissions(shellOutputReceiver.getAdbOutputLines()); - info(Arrays.toString(adbOutputLines.toArray())); - adbOutputLines.forEach(s -> { - try { - device.executeShellCommand("pm grant " + packageName + " " + s, new GenericReceiver(), 15L, TimeUnit.SECONDS); - info(String.format("Permission %s granted on %s", s, device.getName())); - } catch (Exception e) { - error(String.format("Granting %s failed on %s: %s", s, device.getName(), e.getMessage())); - } - }); - return true; - } else { - error(String.format("%s is not installed on %s", packageName, device.getName())); - } - else { - error(String.format("%s must be at least api level 23", device.getName())); - } - } catch (Exception e1) { - error("Granting permissions fail... " + e1.getMessage()); - } - return false; - } - - private boolean deviceHasMarshmallow(IDevice device) { - return device.getVersion().getApiLevel() >= 23; - } - - private List getRequestedPermissions(List list) { - boolean requestedPermissionsSection = false; - List requestPermissions = new ArrayList<>(); - for (String s : list) { - if (!s.contains(".permission.")) { - requestedPermissionsSection = false; - } - if (s.contains("requested permissions:")) { - requestedPermissionsSection = true; - continue; - } - - if (requestedPermissionsSection) { - String permissionName = s.replace(":", "").trim(); - requestPermissions.add(permissionName); - } - } - return requestPermissions; - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/KillCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/KillCommand.java deleted file mode 100644 index a2f7644..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/KillCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.developerphil.adbidea.adb.command.receiver.GenericReceiver; -import com.intellij.openapi.project.Project; -import org.jetbrains.android.facet.AndroidFacet; - -import java.util.concurrent.TimeUnit; - -import static com.developerphil.adbidea.adb.AdbUtil.isAppInstalled; -import static com.developerphil.adbidea.ui.NotificationHelper.error; -import static com.developerphil.adbidea.ui.NotificationHelper.info; - -public class KillCommand implements Command { - @Override - public boolean run(Project project, IDevice device, AndroidFacet facet, String packageName) { - try { - if (isAppInstalled(device, packageName)) { - device.executeShellCommand("am force-stop " + packageName, new GenericReceiver(), 15L, TimeUnit.SECONDS); - info(String.format("%s forced-stop on %s", packageName, device.getName())); - return true; - } else { - error(String.format("%s is not installed on %s", packageName, device.getName())); - } - } catch (Exception e1) { - error("Kill fail... " + e1.getMessage()); - } - return false; - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/RestartPackageCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/RestartPackageCommand.java deleted file mode 100644 index ba7a38a..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/RestartPackageCommand.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -public class RestartPackageCommand extends CommandList { - - public RestartPackageCommand() { - super(new KillCommand(), new StartDefaultActivityCommand(false)); - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/RevokePermissionsAndRestartCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/RevokePermissionsAndRestartCommand.java deleted file mode 100644 index 440610a..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/RevokePermissionsAndRestartCommand.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -public class RevokePermissionsAndRestartCommand extends CommandList { - public RevokePermissionsAndRestartCommand() { - super(new RevokePermissionsCommand(), new StartDefaultActivityCommand(false)); - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/RevokePermissionsCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/RevokePermissionsCommand.java deleted file mode 100644 index 7375c9a..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/RevokePermissionsCommand.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.developerphil.adbidea.adb.command.receiver.GenericReceiver; -import com.intellij.openapi.project.Project; -import org.jetbrains.android.facet.AndroidFacet; - -import java.util.concurrent.TimeUnit; - -import static com.developerphil.adbidea.adb.AdbUtil.isAppInstalled; -import static com.developerphil.adbidea.ui.NotificationHelper.error; -import static com.developerphil.adbidea.ui.NotificationHelper.info; - -public class RevokePermissionsCommand implements Command { - @Override - public boolean run(Project project, IDevice device, AndroidFacet facet, String packageName) { - try { - if (deviceHasMarshmallow(device)) - if (isAppInstalled(device, packageName)) { - GenericReceiver shellOutputReceiver = new GenericReceiver(); - device.executeShellCommand("dumpsys package " + packageName, shellOutputReceiver, 15L, TimeUnit.SECONDS); - shellOutputReceiver.getAdbOutputLines().stream() - //only granted permissions, they come in "android.permission.CAMERA: granted=true" - .filter(s -> s.contains("permission")).filter(s -> s.contains("granted=true")) - //just the permission name is important - .map(s -> s.split(":")[0].trim()) - .forEach(s -> { - try { - device.executeShellCommand("pm revoke " + packageName + " " + s, new GenericReceiver(), 15L, TimeUnit.SECONDS); - info(String.format("Permission %s revoked on %s", s, device.getName())); - } catch (Exception e) { - error(String.format("Revoking %s failed on %s: %s", s, device.getName(), e.getMessage())); - } - }); - return true; - } else { - error(String.format("%s is not installed on %s", packageName, device.getName())); - } - else { - error(String.format("%s must be at least api level 23", device.getName())); - } - } catch (Exception e1) { - error("Revoking permissions fail... " + e1.getMessage()); - } - return false; - } - - private boolean deviceHasMarshmallow(IDevice device) { - return device.getVersion().getApiLevel() >= 23; - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/StartDefaultActivityCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/StartDefaultActivityCommand.java deleted file mode 100644 index b68ca90..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/StartDefaultActivityCommand.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.android.ddmlib.MultiLineReceiver; -import com.android.tools.idea.run.activity.ActivityLocator; -import com.android.tools.idea.run.activity.DefaultActivityLocator; -import com.developerphil.adbidea.adb.ShellCommandsFactory; -import com.developerphil.adbidea.debugger.Debugger; -import com.google.common.base.Joiner; -import com.google.common.base.Strings; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.ThrowableComputable; -import org.jetbrains.android.facet.AndroidFacet; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static com.developerphil.adbidea.ui.NotificationHelper.error; -import static com.developerphil.adbidea.ui.NotificationHelper.info; - -public class StartDefaultActivityCommand implements Command { - - private final boolean withDebugger; - - public StartDefaultActivityCommand(boolean withDebugger) { - this.withDebugger = withDebugger; - } - - @Override - public boolean run(Project project, IDevice device, AndroidFacet facet, String packageName) { - try { - String activityName = getDefaultActivityName(facet, device); - StartActivityReceiver receiver = new StartActivityReceiver(); - String shellCommand = ShellCommandsFactory.startActivity(packageName, activityName, withDebugger); - - device.executeShellCommand(shellCommand, receiver, 15L, TimeUnit.SECONDS); - - if (withDebugger) { - new Debugger(project, device, packageName).attach(); - } - - if (receiver.isSuccess()) { - info(String.format("%s started on %s", packageName, device.getName())); - return true; - } else { - error(String.format("%s could not be started on %s. \n\nADB Output: \n%s", packageName, device.getName(), receiver.getMessage())); - } - } catch (Exception e) { - error("Start fail... " + e.getMessage()); - } - - return false; - } - - private String getDefaultActivityName(final AndroidFacet facet, final IDevice device) throws ActivityLocator.ActivityLocatorException { - return ApplicationManager.getApplication() - .runReadAction((ThrowableComputable) - () -> new DefaultActivityLocator(facet).getQualifiedActivityName(device)); - } - - public static class StartActivityReceiver extends MultiLineReceiver { - - public String message = "Nothing Received"; - - public List currentLines = new ArrayList(); - - @Override - public void processNewLines(String[] strings) { - for (String s : strings) { - if (!Strings.isNullOrEmpty(s)) { - currentLines.add(s); - } - } - computeMessage(); - } - - private void computeMessage() { - message = Joiner.on("\n").join(currentLines); - } - - @Override - public boolean isCancelled() { - return false; - } - - public String getMessage() { - return message; - } - - public boolean isSuccess() { - return currentLines.size() > 0 && currentLines.size() < 3; - } - } - -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/UninstallCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/UninstallCommand.java deleted file mode 100644 index 43986ab..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/UninstallCommand.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import com.android.ddmlib.IDevice; -import com.android.ddmlib.InstallException; -import com.intellij.openapi.project.Project; -import org.jetbrains.android.facet.AndroidFacet; - -import static com.developerphil.adbidea.ui.NotificationHelper.error; -import static com.developerphil.adbidea.ui.NotificationHelper.info; - -public class UninstallCommand implements Command { - - private String mPackageName; - - public UninstallCommand() { - } - - public UninstallCommand(String packageName) { - mPackageName = packageName; - } - - @Override - public boolean run(Project project, IDevice device, AndroidFacet facet, String packageName) { - if (mPackageName != null) { - packageName = mPackageName; - } - try { - String errorCode = device.uninstallPackage(packageName); - if (errorCode == null) { - info(String.format("%s uninstalled on %s", packageName, device.getName())); - return true; - } else { - error(String.format("%s is not installed on %s", packageName, device.getName())); - } - } catch (InstallException e1) { - error("Uninstall fail... " + e1.getMessage()); - } - return false; - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/receiver/GenericReceiver.java b/src/main/java/com/developerphil/adbidea/adb/command/receiver/GenericReceiver.java deleted file mode 100644 index bc7d869..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/receiver/GenericReceiver.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.developerphil.adbidea.adb.command.receiver; - -import com.android.ddmlib.MultiLineReceiver; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class GenericReceiver extends MultiLineReceiver { - - private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$ - private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$ - - private String mErrorMessage = null; - - private List adbOutputLines = new ArrayList(); - - public GenericReceiver() { - } - - @Override - public void processNewLines(String[] lines) { - this.adbOutputLines.addAll(Arrays.asList(lines)); - - for (String line : lines) { - if (!line.isEmpty()) { - if (line.startsWith(SUCCESS_OUTPUT)) { - mErrorMessage = null; - } else { - Matcher m = FAILURE_PATTERN.matcher(line); - if (m.matches()) { - mErrorMessage = m.group(1); - } else { - mErrorMessage = "Unknown failure"; - } - } - } - } - } - - @Override - public boolean isCancelled() { - return false; - } - - public List getAdbOutputLines() { - return adbOutputLines; - } - - public boolean isSuccess() { - return mErrorMessage == null; - } - - public String getErrorMessage() { - return mErrorMessage; - } -} diff --git a/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java b/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java deleted file mode 100644 index c04da93..0000000 --- a/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.developerphil.adbidea.adb.command.receiver; - -import com.android.ddmlib.IShellOutputReceiver; -import com.developerphil.adbidea.ui.Utils; -import com.google.common.base.Charsets; - -public class PrintReceiver implements IShellOutputReceiver { - - private String mString; - - public final void addOutput(byte[] data, int offset, int length) { - if (!this.isCancelled()) { - mString = new String(data, offset, length, Charsets.UTF_8) + "\r\n"; - } - } - - @Override - public void flush() { - - } - - public void done() { - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public String toString() { - return Utils.isEmpty(mString) ? "" : mString; - } -} diff --git a/src/main/java/com/developerphil/adbidea/compatibility/BackwardCompatibleGetter.java b/src/main/java/com/developerphil/adbidea/compatibility/BackwardCompatibleGetter.java deleted file mode 100644 index da22016..0000000 --- a/src/main/java/com/developerphil/adbidea/compatibility/BackwardCompatibleGetter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.developerphil.adbidea.compatibility; - -import org.joor.ReflectException; - -/** - * Abstracts the logic to call the current implementation and fall back on reflection for previous versions - */ -public abstract class BackwardCompatibleGetter { - - public final T get() { - try { - return getCurrentImplementation(); - } catch (LinkageError error) { - return getPreviousImplementation(); - } catch (Throwable e) { - if (isReflectiveException(e)) { - return getPreviousImplementation(); - } else { - throw new RuntimeException(e); - } - } - } - - private boolean isReflectiveException(Throwable t) { - return t instanceof ClassNotFoundException || - t instanceof NoSuchFieldException || - t instanceof LinkageError || - t instanceof NoSuchMethodException || - t instanceof ReflectException - ; - } - - protected abstract T getCurrentImplementation() throws Throwable; - - protected abstract T getPreviousImplementation(); -} diff --git a/src/main/java/com/developerphil/adbidea/compatibility/CanRunOnDeviceCompat.kt b/src/main/java/com/developerphil/adbidea/compatibility/CanRunOnDeviceCompat.kt deleted file mode 100644 index aa00e06..0000000 --- a/src/main/java/com/developerphil/adbidea/compatibility/CanRunOnDeviceCompat.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.developerphil.adbidea.compatibility - -import com.android.ddmlib.IDevice -import com.android.tools.idea.run.* -import org.jetbrains.android.facet.AndroidFacet -import org.joor.on -import org.joor.asType - -class CanRunOnDeviceCompat(private val myFacet: AndroidFacet, device: IDevice) : BackwardCompatibleGetter() { - - private val androidDevice = ConnectedAndroidDevice(device, null) - - override// Android Studio 3.4+ - fun getCurrentImplementation() = on() - .call("create", myFacet, null, null) - .asType() - .validate(androidDevice) - - override// Android Studio 3.3 & Intellij 2018.3 - fun getPreviousImplementation() = on() - .call("create", myFacet) - .asType() - .validate(androidDevice) -} diff --git a/src/main/java/com/developerphil/adbidea/compatibility/IsWatchFeatureRequiredCompat.java b/src/main/java/com/developerphil/adbidea/compatibility/IsWatchFeatureRequiredCompat.java deleted file mode 100644 index 0321b40..0000000 --- a/src/main/java/com/developerphil/adbidea/compatibility/IsWatchFeatureRequiredCompat.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.developerphil.adbidea.compatibility; - -import org.jetbrains.android.facet.AndroidFacet; -import org.joor.Reflect; - -import static com.android.tools.idea.run.util.LaunchUtils.isWatchFeatureRequired; - -public class IsWatchFeatureRequiredCompat extends BackwardCompatibleGetter { - - private AndroidFacet facet; - - public IsWatchFeatureRequiredCompat(AndroidFacet facet) { - this.facet = facet; - } - - @Override - // Android studio 2.0-Preview5 - protected Boolean getCurrentImplementation() throws Throwable { - return isWatchFeatureRequired(facet); - } - - @Override - // Android studio 2.0 - protected Boolean getPreviousImplementation() { - return Reflect.on("com.android.tools.idea.run.LaunchUtils").call("isWatchFeatureRequired", facet).get(); - } - -} diff --git a/src/main/java/com/developerphil/adbidea/dagger/PluginComponent.java b/src/main/java/com/developerphil/adbidea/dagger/PluginComponent.java deleted file mode 100644 index 575165b..0000000 --- a/src/main/java/com/developerphil/adbidea/dagger/PluginComponent.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.developerphil.adbidea.dagger; - -import com.developerphil.adbidea.adb.DeviceResultFetcher; -import com.developerphil.adbidea.ui.DeviceChooserDialog; -import dagger.Component; - -import javax.inject.Singleton; - -@Singleton -@Component(modules = PluginModule.class) -public interface PluginComponent { - void inject(DeviceChooserDialog deviceChooserDialog); - - DeviceResultFetcher getDeviceResultFetcher(); -} diff --git a/src/main/java/com/developerphil/adbidea/dagger/PluginModule.java b/src/main/java/com/developerphil/adbidea/dagger/PluginModule.java deleted file mode 100644 index 8859d10..0000000 --- a/src/main/java/com/developerphil/adbidea/dagger/PluginModule.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.developerphil.adbidea.dagger; - -import com.developerphil.adbidea.PluginPreferences; -import com.developerphil.adbidea.PluginPreferencesImpl; -import com.developerphil.adbidea.accessor.preference.PreferenceAccessor; -import com.developerphil.adbidea.accessor.preference.ProjectPreferenceAccessor; -import com.developerphil.adbidea.adb.Bridge; -import com.developerphil.adbidea.adb.BridgeImpl; -import com.developerphil.adbidea.adb.DeviceResultFetcher; -import com.developerphil.adbidea.adb.UseSameDevicesHelper; -import com.intellij.openapi.project.Project; -import dagger.Module; -import dagger.Provides; - -import javax.inject.Singleton; - -@Module -public class PluginModule { - - private final Project project; - - public PluginModule(Project project) { - this.project = project; - } - - @Singleton - @Provides Project provideProject() { - return project; - } - - @Singleton - @Provides - PreferenceAccessor providePreferenceAccessor() { - return new ProjectPreferenceAccessor(project); - } - - @Singleton - @Provides - PluginPreferences providePluginPreferences(PreferenceAccessor preferenceAccessor) { - return new PluginPreferencesImpl(preferenceAccessor); - } - - @Singleton - @Provides - UseSameDevicesHelper provideUseSameDeviceController(PluginPreferences preferences, Bridge bridge) { - return new UseSameDevicesHelper(preferences, bridge); - } - - @Provides - DeviceResultFetcher provideDeviceResultFetcher(UseSameDevicesHelper useSameDevicesHelper, Bridge bridge) { - return new DeviceResultFetcher(project, useSameDevicesHelper, bridge); - } - - @Provides - Bridge provideBridge(BridgeImpl bridgeImpl) { - return bridgeImpl; - } - -} diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java index 2315d57..aa53521 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java @@ -133,10 +133,10 @@ public void mouseClicked(MouseEvent e) { sb.append(PARAMETER_UNINSTALLED); } String keywordText = tv_keyword.getText(); - if (!Utils.isEmpty(keywordText)) { + if (!Utils.Companion.isEmpty(keywordText)) { sb.append(keywordText); } - AdbFacade.getAllApplicationList(mProject, sb.toString(), strings -> { + AdbFacade.INSTANCE.getAllApplicationList(mProject, sb.toString(), strings -> { Collections.sort(strings); mList = strings; mModel = new MyApplistModel(mList); @@ -149,7 +149,7 @@ public void mouseClicked(MouseEvent e) { String[] selected = new String[selectedIndices.length]; for (int i = 0; i < selectedIndices.length; i++) { String packageName = mList.get(selectedIndices[i]); - AdbFacade.uninstall(mProject, getRealPackageName(packageName)); + AdbFacade.INSTANCE.uninstall(mProject, getRealPackageName(packageName)); selected[i] = packageName; } for (String s : selected) { @@ -160,16 +160,16 @@ public void mouseClicked(MouseEvent e) { mClearAppCacheDataButton.addActionListener(e -> { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { - AdbFacade.clearData(mProject, getRealPackageName(packageName)); + AdbFacade.INSTANCE.clearData(mProject, getRealPackageName(packageName)); } }); mViewDetailButton.addActionListener(e -> { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - Utils.append2TextPane("View " + name + " detail : \n", JBColor.BLUE, tp); - AdbFacade.getPackageDetail(mProject, name, s -> { - Utils.append2TextPane(s, tp); + Utils.Companion.append2TextPane("View " + name + " detail : \n", JBColor.BLUE, tp); + AdbFacade.INSTANCE.getPackageDetail(mProject, name, s -> { + Utils.Companion.append2TextPane(s, tp); return null; }); } @@ -178,9 +178,9 @@ public void mouseClicked(MouseEvent e) { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - Utils.append2TextPane("View " + name + " apk Path : \n", JBColor.BLUE, tp); - AdbFacade.getPackagePath(mProject, name, s -> { - Utils.append2TextPane(s, tp); + Utils.Companion.append2TextPane("View " + name + " apk Path : \n", JBColor.BLUE, tp); + AdbFacade.INSTANCE.getPackagePath(mProject, name, s -> { + Utils.Companion.append2TextPane(s, tp); return null; }); } @@ -189,25 +189,25 @@ public void mouseClicked(MouseEvent e) { List selectedValuesList = mJList.getSelectedValuesList(); if (selectedValuesList.isEmpty()) { String keywordText = tv_keyword.getText(); - if (!Utils.isEmpty(keywordText)) { - Utils.append2TextPane("View running services related with " + keywordText + " : \n", JBColor.BLUE, tp); - AdbFacade.getActivityService(mProject, keywordText, s -> { - Utils.append2TextPane(s, tp); + if (!Utils.Companion.isEmpty(keywordText)) { + Utils.Companion.append2TextPane("View running services related with " + keywordText + " : \n", JBColor.BLUE, tp); + AdbFacade.INSTANCE.getActivityService(mProject, keywordText, s -> { + Utils.Companion.append2TextPane(s, tp); return null; }); } else { - Utils.append2TextPane("View all running services : \n", JBColor.BLUE, tp); - AdbFacade.getActivityService(mProject, "", s -> { - Utils.append2TextPane(s, tp); + Utils.Companion.append2TextPane("View all running services : \n", JBColor.BLUE, tp); + AdbFacade.INSTANCE.getActivityService(mProject, "", s -> { + Utils.Companion.append2TextPane(s, tp); return null; }); } } for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - Utils.append2TextPane("View running services related with " + name + " : \n", JBColor.BLUE, tp); - AdbFacade.getActivityService(mProject, name, s -> { - Utils.append2TextPane(s, tp); + Utils.Companion.append2TextPane("View running services related with " + name + " : \n", JBColor.BLUE, tp); + AdbFacade.INSTANCE.getActivityService(mProject, name, s -> { + Utils.Companion.append2TextPane(s, tp); return null; }); } @@ -216,17 +216,17 @@ public void mouseClicked(MouseEvent e) { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - Utils.append2TextPane("Force-stop : " + name + "\n", JBColor.BLUE, tp); - AdbFacade.forceStop(mProject, name); + Utils.Companion.append2TextPane("Force-stop : " + name + "\n", JBColor.BLUE, tp); + AdbFacade.INSTANCE.forceStop(mProject, name); } }); mForegroundActivityButton.addActionListener(e -> { - Utils.append2TextPane("Foreground Activity : \n", JBColor.BLUE, tp); - AdbFacade.showForegroundActivity(mProject, s -> { - if (Utils.isEmpty(s)) { - Utils.append2TextPaneNewLine("get foreground Activity failure",JBColor.RED, tp); + Utils.Companion.append2TextPane("Foreground Activity : \n", JBColor.BLUE, tp); + AdbFacade.INSTANCE.showForegroundActivity(mProject, s -> { + if (Utils.Companion.isEmpty(s)) { + Utils.Companion.append2TextPaneNewLine("get foreground Activity failure",JBColor.RED, tp); } else { - Utils.append2TextPaneNewLine(s, tp); + Utils.Companion.append2TextPaneNewLine(s, tp); } return null; }); @@ -236,7 +236,7 @@ public void mouseClicked(MouseEvent e) { String name = ""; if (!selectedValuesList.isEmpty()) { name = getRealPackageName(selectedValuesList.get(0)); - Utils.append2TextPane("Monkey test of " + name + " :\n", JBColor.BLUE,tp); + Utils.Companion.append2TextPane("Monkey test of " + name + " :\n", JBColor.BLUE,tp); } String countStr = JOptionPane.showInputDialog("Enter test count(only integers):"); if (countStr.isEmpty()) { @@ -251,8 +251,8 @@ public void mouseClicked(MouseEvent e) { return; } if (count > 0) { - AdbFacade.monkeyTest(mProject, name,count, s -> { - Utils.append2TextPane(s, tp); + AdbFacade.INSTANCE.monkeyTest(mProject, name,count, s -> { + Utils.Companion.append2TextPane(s, tp); return null; }); } diff --git a/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java b/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java deleted file mode 100644 index 950a21b..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.developerphil.adbidea.ui; - -import com.developerphil.adbidea.bean.BoundDataType; -import com.developerphil.adbidea.bean.BoundItemBean; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import javax.swing.table.AbstractTableModel; -import org.apache.commons.lang3.StringUtils; - -public class BoundTableModel extends AbstractTableModel{ - private Class[] cellType = { Boolean.class, String.class, String.class, BoundDataType.class }; - private String title[] = { "select", "key", "value" ,"type"}; - - private List data = new ArrayList<>(); - - public BoundTableModel() { - } - - - public List getData() { - return data.stream().filter(BoundItemBean -> BoundItemBean.getSelected()&&StringUtils.isNotEmpty(BoundItemBean.getKey())).collect(Collectors.toList()); - } - - public void setData(List data) { - this.data.clear(); - this.data.addAll(data); - fireTableDataChanged(); - } - - public void clear() { - int size = this.data.size(); - this.data.clear(); - fireTableRowsDeleted(0, size); - } - - public void addEmptyRow() { - data.add(new BoundItemBean(true, "", "",BoundDataType.STRING)); - fireTableRowsInserted(data.size() - 1, data.size() - 1); - } - - public void removeRow(int row) { - if (row > -1 && row < data.size()) { - data.remove(row); - } - fireTableRowsDeleted(row, row); - } - - @Override - public Class getColumnClass(int arg0) { - return cellType[arg0]; - } - - @Override - public String getColumnName(int arg0) { - return title[arg0]; - } - - @Override - public int getColumnCount() { - return title.length; - } - - @Override - public int getRowCount() { - return data.size(); - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (rowIndex < data.size()) { - switch (columnIndex) { - case 0: - return data.get(rowIndex).getSelected(); - case 1: - return data.get(rowIndex).getKey(); - case 2: - return data.get(rowIndex).getValue(); - case 3: - return data.get(rowIndex).getDataType(); - } - } - return null; - } - - //重写isCellEditable方法 - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - return true; - } - - //重写setValueAt方法 - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - if (rowIndex < data.size()) { - switch (columnIndex) { - case 0: - data.get(rowIndex).setSelected((Boolean) aValue); - break; - case 1: - data.get(rowIndex).setKey((String) aValue); - break; - case 2: - data.get(rowIndex).setValue((String) aValue); - break; - case 3: - data.get(rowIndex).setDataType((BoundDataType) aValue); - break; - } - } - this.fireTableCellUpdated(rowIndex, columnIndex); - } - - -} diff --git a/src/main/java/com/developerphil/adbidea/ui/BoundTypeCellEditor.java b/src/main/java/com/developerphil/adbidea/ui/BoundTypeCellEditor.java deleted file mode 100644 index c3b6256..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/BoundTypeCellEditor.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.developerphil.adbidea.ui; - -import com.developerphil.adbidea.bean.BoundDataType; -import javax.swing.DefaultCellEditor; -import javax.swing.JComboBox; - -/** - * Created by XQ Yang on 10/10/2018 2:12 PM. - * Description : - */ - -public class BoundTypeCellEditor extends DefaultCellEditor { - - public BoundTypeCellEditor() { - super(new JComboBox<>(BoundDataType.values())); - } -} diff --git a/src/main/java/com/developerphil/adbidea/ui/BoundTypeJTable.java b/src/main/java/com/developerphil/adbidea/ui/BoundTypeJTable.java deleted file mode 100644 index 16af95c..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/BoundTypeJTable.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.developerphil.adbidea.ui; - -import javax.swing.JTable; -import javax.swing.table.TableCellEditor; - -/** - * Created by XQ Yang on 10/10/2018 2:08 PM. - * Description : - */ - -public class BoundTypeJTable extends JTable { - - private BoundTypeCellEditor mBoundTypeCellEditor= new BoundTypeCellEditor(); - - @Override - public TableCellEditor getCellEditor(int row, int column) { - if (column == 3) { - return mBoundTypeCellEditor; - } - return super.getCellEditor(row, column); - } -} - diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java b/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java index f2beb61..5098b53 100644 --- a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java @@ -12,7 +12,6 @@ import java.awt.Dimension; import java.awt.Insets; import java.util.stream.Collectors; -import javax.inject.Inject; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JPanel; @@ -31,7 +30,6 @@ public class DeviceChooserDialog extends DialogWrapper { private JPanel myDeviceChooserWrapper; private JCheckBox useSameDeviceSCheckBox; - @Inject PluginPreferences pluginPreferences; public DeviceChooserDialog(@NotNull final AndroidFacet facet) { @@ -39,11 +37,11 @@ public DeviceChooserDialog(@NotNull final AndroidFacet facet) { setTitle(AndroidBundle.message("choose.device.dialog.title")); myProject = facet.getModule().getProject(); - myProject.getComponent(ObjectGraph.class).inject(this); + pluginPreferences = myProject.getComponent(ObjectGraph.class).getPluginPreferences(); getOKAction().setEnabled(false); - myDeviceChooser = new MyDeviceChooser(true, getOKAction(), facet, facet.getConfiguration().getAndroidTarget(), null); + myDeviceChooser = new MyDeviceChooser(true, getOKAction(), facet, null); Disposer.register(myDisposable, myDeviceChooser); myDeviceChooser.addListener(this :: updateOkButton); diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserListener.java b/src/main/java/com/developerphil/adbidea/ui/DeviceChooserListener.java deleted file mode 100644 index caf8cf8..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.developerphil.adbidea.ui; - -/** - * @author Eugene.Kudelevsky - */ -public interface DeviceChooserListener { - void selectedDevicesChanged(); -} diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java index f149025..c1af001 100644 --- a/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java @@ -61,22 +61,22 @@ public void mouseClicked(MouseEvent e) { } }); //how to switch thread? - AdbFacade.getSimpleInfo(mProject, "getprop ro.product.brand", "get Device brand ", s -> { - Utils.append2TextPaneNewLine("Device brand:", JBColor.BLUE, mTextPane); - Utils.append2TextPane(s, mTextPane); + AdbFacade.INSTANCE.getSimpleInfo(mProject, "getprop ro.product.brand", "get Device brand ", s -> { + Utils.Companion.append2TextPaneNewLine("Device brand:", JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPane(s, mTextPane); return null; }); - AdbFacade.getSimpleInfo(mProject, "getprop ro.product.model", "get Device model ", s1 -> { - Utils.append2TextPaneNewLine("Device model:", JBColor.BLUE, mTextPane); - Utils.append2TextPane(s1, mTextPane); + AdbFacade.INSTANCE.getSimpleInfo(mProject, "getprop ro.product.model", "get Device model ", s1 -> { + Utils.Companion.append2TextPaneNewLine("Device model:", JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPane(s1, mTextPane); return null; }); - AdbFacade.getSimpleInfo(mProject, "getprop ro.product.name", "get Device name ", s2 -> { - Utils.append2TextPaneNewLine("Device name:", JBColor.BLUE, mTextPane); - Utils.append2TextPaneNewLine(s2, mTextPane); + AdbFacade.INSTANCE.getSimpleInfo(mProject, "getprop ro.product.name", "get Device name ", s2 -> { + Utils.Companion.append2TextPaneNewLine("Device name:", JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPaneNewLine(s2, mTextPane); return null; }); - AdbFacade.getSimpleInfo(mProject, "getprop ro.build.version.release", "get Android Version ", s1 -> { + AdbFacade.INSTANCE.getSimpleInfo(mProject, "getprop ro.build.version.release", "get Android Version ", s1 -> { androidVersion = s1.trim(); return null; }); @@ -101,42 +101,42 @@ public void mouseClicked(MouseEvent e) { }); mSystemButton.addActionListener(e -> { getInfo2Show("Android id:", "settings get secure android_id", "get Android id "); - AdbFacade.getSimpleInfo(mProject, "getprop ro.build.version.sdk", "get Android sdk ", sdk -> { - Utils.append2TextPaneNewLine("Android SDK:", JBColor.BLUE, mTextPane); - Utils.append2TextPane(sdk, mTextPane); + AdbFacade.INSTANCE.getSimpleInfo(mProject, "getprop ro.build.version.sdk", "get Android sdk ", sdk -> { + Utils.Companion.append2TextPaneNewLine("Android SDK:", JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPane(sdk, mTextPane); return null; }); - if (!Utils.isEmpty(androidVersion)) { - Utils.append2TextPaneNewLine("Android Version:", JBColor.BLUE, mTextPane); - Utils.append2TextPaneNewLine(androidVersion, mTextPane); + if (!Utils.Companion.isEmpty(androidVersion)) { + Utils.Companion.append2TextPaneNewLine("Android Version:", JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPaneNewLine(androidVersion, mTextPane); } }); mNetworkButton.addActionListener(e -> { getInfo2Show("Android id:", "settings get secure android_id", "get Android id "); - if (!Utils.isEmpty(androidVersion)) { + if (!Utils.Companion.isEmpty(androidVersion)) { try { int version = Integer.parseInt(androidVersion.substring(0, 1)); if (version >= 5) { //cannot work //getInfo2Show("IMEI 1 :", "service call iphonesubinfo 1 | awk -F \"'\" '{print $2}' | sed '1 d' | tr -d '.' | awk '{print}' ORS=", "get IMEI 1 "); - Utils.append2TextPaneNewLine("Can not get IMEI", JBColor.red, mTextPane); + Utils.Companion.append2TextPaneNewLine("Can not get IMEI", JBColor.red, mTextPane); } else { getInfo2Show("IMEI 1 :", "dumpsys iphonesubinfo", "get IMEI 1 "); } } catch (NumberFormatException e1) { e1.printStackTrace(); - Utils.append2TextPaneNewLine("Can not get IMEI", JBColor.red, mTextPane); + Utils.Companion.append2TextPaneNewLine("Can not get IMEI", JBColor.red, mTextPane); } } - AdbFacade.getSimpleInfo(mProject, "ifconfig | grep Mask", "get IP Address ", s -> { - Utils.append2TextPaneNewLine("ifconfig :", JBColor.BLUE, mTextPane); - Utils.append2TextPaneNewLine(s, mTextPane); + AdbFacade.INSTANCE.getSimpleInfo(mProject, "ifconfig | grep Mask", "get IP Address ", s -> { + Utils.Companion.append2TextPaneNewLine("ifconfig :", JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPaneNewLine(s, mTextPane); return null; }); - AdbFacade.getSimpleInfo(mProject, "ifconfig wlan0", "get IP Address ", s1 -> { - Utils.append2TextPaneNewLine("wlan0 :", JBColor.BLUE, mTextPane); - Utils.append2TextPaneNewLine(s1, mTextPane); + AdbFacade.INSTANCE.getSimpleInfo(mProject, "ifconfig wlan0", "get IP Address ", s1 -> { + Utils.Companion.append2TextPaneNewLine("wlan0 :", JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPaneNewLine(s1, mTextPane); return null; }); getInfo2Show("Mac Address:", "cat /sys/class/net/wlan0/address", "get Mac Address "); @@ -146,9 +146,9 @@ public void mouseClicked(MouseEvent e) { } public void getInfo2Show(String item, String command, String desc) { - AdbFacade.getSimpleInfo(mProject, command, desc, s -> { - Utils.append2TextPaneNewLine(item, JBColor.BLUE, mTextPane); - Utils.append2TextPaneNewLine(s, mTextPane); + AdbFacade.INSTANCE.getSimpleInfo(mProject, command, desc, s -> { + Utils.Companion.append2TextPaneNewLine(item, JBColor.BLUE, mTextPane); + Utils.Companion.append2TextPaneNewLine(s, mTextPane); return null; }); } diff --git a/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.java b/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.java index c09db12..8bcc0ce 100644 --- a/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.java @@ -65,7 +65,7 @@ public InteractingFrame(@Nullable Project project) { mGoButton.addActionListener(e -> { Object selectedItem = mCbComponent.getSelectedItem(); String name = selectedItem != null ? selectedItem.toString() : ""; - if (!mSendBroadcastRadioButton.isSelected() && Utils.isEmpty(name)) { + if (!mSendBroadcastRadioButton.isSelected() && Utils.Companion.isEmpty(name)) { HelperMethodsKt.showErrorMsg("Component name cannot be empty"); return; } @@ -75,7 +75,7 @@ public InteractingFrame(@Nullable Project project) { String category = selectedItem != null ? selectedItem.toString() : ""; List boundData = mModel.getData(); int type = mStartActivityRadioButton.isSelected() ? 0 : (mStartServiceRadioButton.isSelected() ? 1 : 2); - AdbFacade.interacting(mProject, type, action, category, name, boundData); + AdbFacade.INSTANCE.interacting(mProject, type, action, category, name, boundData); }); setContentPane(mPanel); diff --git a/src/main/java/com/developerphil/adbidea/ui/ModuleChooserDialogHelper.java b/src/main/java/com/developerphil/adbidea/ui/ModuleChooserDialogHelper.java deleted file mode 100644 index 620bfa1..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/ModuleChooserDialogHelper.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.developerphil.adbidea.ui; - -import com.google.common.collect.Lists; -import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.project.Project; -import java.util.List; -import org.jetbrains.android.facet.AndroidFacet; - -public final class ModuleChooserDialogHelper { - - public static final String SELECTED_MODULE_PROPERTY = ModuleChooserDialogHelper.class.getCanonicalName() + "-SELECTED_MODULE"; - public static final String DEFAULT_MODULE_PROPERTY = ModuleChooserDialogHelper.class.getCanonicalName() + "-DEFAULT_MODULE"; - - public static final String DO_NOT_SELECT_THE_DEFAULT_MODULE = "Do not select the default module"; - - - private ModuleChooserDialogHelper() { - } - - public static AndroidFacet showDialogForFacets(Project project, List facets,boolean isSetDefault) { - List modules = Lists.newArrayList(); - List modulesName = Lists.newArrayList(); - String previousModuleName = getPreviousModuleName(project); - String defaultModuleName = getDefaultModuleName(project); - List previousSelectedModule = null; - for (AndroidFacet facet : facets) { - Module module = facet.getModule(); - String name = module.getName(); - if (!isSetDefault && name.equals(defaultModuleName)) { - return facet; - } - modules.add(module); - modulesName.add(name); - if (name.equals(previousModuleName)) { - previousSelectedModule = Lists.newArrayList(name); - } else if (isSetDefault && name.equals(defaultModuleName)) { - previousSelectedModule = Lists.newArrayList(name); - } - } - - if (isSetDefault && previousSelectedModule == null) { - previousSelectedModule = Lists.newArrayList(DO_NOT_SELECT_THE_DEFAULT_MODULE); - } - - if (isSetDefault) { - modulesName.add(DO_NOT_SELECT_THE_DEFAULT_MODULE); - } - String titleSelectDefault = "Choose Default Module"; - if (!Utils.isEmpty(defaultModuleName)) { - titleSelectDefault += ",Current module is :" + defaultModuleName; - } - MyChooseModulesDialog dialog = new MyChooseModulesDialog(project, modulesName, isSetDefault? titleSelectDefault :"Choose Module", isSetDefault?"Set the default module for each operation":"", ModuleType.get(modules.get(0)).getIcon()); - dialog.setSingleSelectionMode(); - if (previousSelectedModule != null) { - dialog.selectElements(previousSelectedModule); - } - dialog.show(); - - List chosenElements = dialog.getChosenElements(); - if (chosenElements.isEmpty()) { - return null; - } - - String chosenModule = chosenElements.get(0); - if (isSetDefault) { - if (chosenModule.equals(DO_NOT_SELECT_THE_DEFAULT_MODULE)) { - saveDefaultModuleName(project,""); - return null; - } else { - saveDefaultModuleName(project,chosenModule); - } - } else { - saveModuleName(project, chosenModule); - } - int chosenModuleIndex = modulesName.indexOf(chosenModule); - return facets.get(chosenModuleIndex); - } - - private static void saveDefaultModuleName(Project project, String moduleName) { - final PropertiesComponent properties = PropertiesComponent.getInstance(project); - properties.setValue(DEFAULT_MODULE_PROPERTY, moduleName); - } - - private static void saveModuleName(Project project, String moduleName) { - final PropertiesComponent properties = PropertiesComponent.getInstance(project); - properties.setValue(SELECTED_MODULE_PROPERTY, moduleName); - } - - private static String getPreviousModuleName(Project project) { - final PropertiesComponent properties = PropertiesComponent.getInstance(project); - return properties.getValue(SELECTED_MODULE_PROPERTY); - } - - private static String getDefaultModuleName(Project project) { - final PropertiesComponent properties = PropertiesComponent.getInstance(project); - return properties.getValue(DEFAULT_MODULE_PROPERTY); - } - - -} diff --git a/src/main/java/com/developerphil/adbidea/ui/MyApplistModel.java b/src/main/java/com/developerphil/adbidea/ui/MyApplistModel.java deleted file mode 100644 index cec36ae..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/MyApplistModel.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.developerphil.adbidea.ui; - -import java.util.List; -import javax.swing.AbstractListModel; - -/** - * Created by XQ Yang on 10/8/2018 5:41 PM. - * Description : - */ - -class MyApplistModel extends AbstractListModel { - - private List mList; - - public MyApplistModel(List list) { - mList = list; - } - - @Override - public int getSize() { - return mList.size(); - } - - @Override - public String getElementAt(int index) { - return mList.get(index); - } - - public void delete(String s) { - int index = mList.indexOf(s); - if (index != -1) { - mList.remove(index); - fireIntervalRemoved(this, index, index); - } - } -} diff --git a/src/main/java/com/developerphil/adbidea/ui/MyChooseModulesDialog.java b/src/main/java/com/developerphil/adbidea/ui/MyChooseModulesDialog.java deleted file mode 100644 index f64b9d9..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/MyChooseModulesDialog.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.developerphil.adbidea.ui; - -import com.intellij.icons.AllIcons; -import com.intellij.ide.util.ChooseElementsDialog; -import com.intellij.openapi.project.Project; -import java.util.List; -import javax.swing.Icon; -import org.jetbrains.annotations.Nullable; - -public class MyChooseModulesDialog extends ChooseElementsDialog { - - private Icon mIcon; - - public MyChooseModulesDialog(Project project, List items, String title, String description, Icon icon) { - super(project, items, title, description, false); - mIcon = icon; - } - - public void setSingleSelectionMode() { - this.myChooser.setSingleSelectionMode(); - } - - @Override - protected String getItemText(String s) { - return s; - } - - @Nullable - @Override - protected Icon getItemIcon(String s) { - return s.equals(ModuleChooserDialogHelper.DO_NOT_SELECT_THE_DEFAULT_MODULE) ? AllIcons.Actions.Clear: mIcon; - } -} \ No newline at end of file diff --git a/src/main/java/com/developerphil/adbidea/ui/MyDeviceChooser.java b/src/main/java/com/developerphil/adbidea/ui/MyDeviceChooser.java deleted file mode 100755 index e86cb2e..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/MyDeviceChooser.java +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright 2000-2010 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.developerphil.adbidea.ui; - -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.IDevice; -import com.android.sdklib.AndroidVersion; -import com.android.sdklib.IAndroidTarget; -import com.android.tools.idea.model.AndroidModuleInfo; -import com.android.tools.idea.run.LaunchCompatibility; -import com.developerphil.adbidea.compatibility.CanRunOnDeviceCompat; -import com.developerphil.adbidea.compatibility.IsWatchFeatureRequiredCompat; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.ui.ColoredTableCellRenderer; -import com.intellij.ui.DoubleClickListener; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.table.JBTable; -import com.intellij.util.Alarm; -import com.intellij.util.ArrayUtil; -import com.intellij.util.ThreeState; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.HashSet; -import gnu.trove.TIntArrayList; -import org.jetbrains.android.dom.AndroidAttributeValue; -import org.jetbrains.android.dom.manifest.UsesFeature; -import org.jetbrains.android.facet.AndroidFacet; -import org.jetbrains.android.sdk.AndroidSdkUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.AbstractTableModel; -import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.util.*; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import static com.intellij.openapi.util.text.StringUtil.capitalize; - -/** - * @author Eugene.Kudelevsky - */ -public class MyDeviceChooser implements Disposable { - private static final String[] COLUMN_TITLES = new String[]{"Device", "Serial Number", "State", "Compatible"}; - private static final int DEVICE_NAME_COLUMN_INDEX = 0; - private static final int SERIAL_COLUMN_INDEX = 1; - private static final int DEVICE_STATE_COLUMN_INDEX = 2; - private static final int COMPATIBILITY_COLUMN_INDEX = 3; - private static final int REFRESH_INTERVAL_MS = 500; - - public static final IDevice[] EMPTY_DEVICE_ARRAY = new IDevice[0]; - - private final List myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private final Alarm myRefreshingAlarm; - private final AndroidDebugBridge myBridge; - - private volatile boolean myProcessSelectionFlag = true; - - /** The current list of devices that is displayed in the table. */ - private IDevice[] myDisplayedDevices = EMPTY_DEVICE_ARRAY; - - /** - * The current list of devices obtained from the debug bridge. This is updated in a background thread. - * If it is different than {@link #myDisplayedDevices}, then a {@link #refreshTable} invocation in the EDT thread - * will update the displayed list to match the detected list. - */ - private AtomicReference myDetectedDevicesRef = new AtomicReference(EMPTY_DEVICE_ARRAY); - - private JComponent myPanel; - private JBTable myDeviceTable; - - private final AndroidFacet myFacet; - private final Condition myFilter; - private final AndroidVersion myMinSdkVersion; - private final IAndroidTarget myProjectTarget; - private final EnumSet myRequiredHardwareFeatures; - - private int[] mySelectedRows; - private boolean hadUserInteraction = false; - @Nullable - private String[] previouslySelectedSerials; - - public MyDeviceChooser(boolean multipleSelection, - @NotNull final Action okAction, - @NotNull AndroidFacet facet, - @NotNull IAndroidTarget projectTarget, - @Nullable Condition filter) { - - myFacet = facet; - myFilter = filter; - myMinSdkVersion = AndroidModuleInfo.getInstance(facet).getRuntimeMinSdkVersion(); - myProjectTarget = projectTarget; - if (new IsWatchFeatureRequiredCompat(facet).get()) { - myRequiredHardwareFeatures = EnumSet.of(IDevice.HardwareFeature.WATCH); - } else { - myRequiredHardwareFeatures = EnumSet.noneOf(IDevice.HardwareFeature.class); - } - - myDeviceTable = new JBTable(); - myPanel = ScrollPaneFactory.createScrollPane(myDeviceTable); - myPanel.setPreferredSize(new Dimension(450, 220)); - - myDeviceTable.setModel(new MyDeviceTableModel(EMPTY_DEVICE_ARRAY)); - myDeviceTable.setSelectionMode(multipleSelection ? - ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : - ListSelectionModel.SINGLE_SELECTION); - myDeviceTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - if (myProcessSelectionFlag) { - hadUserInteraction = true; - fireSelectedDevicesChanged(); - } - } - }); - new DoubleClickListener() { - @Override - protected boolean onDoubleClick(MouseEvent e) { - if (myDeviceTable.isEnabled() && okAction.isEnabled()) { - okAction.actionPerformed(null); - return true; - } - return false; - } - }.installOn(myDeviceTable); - - myDeviceTable.setDefaultRenderer(LaunchCompatibility.class, new LaunchCompatibilityRenderer()); - myDeviceTable.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER && okAction.isEnabled()) { - okAction.actionPerformed(null); - } - } - }); - - setColumnWidth(myDeviceTable, DEVICE_NAME_COLUMN_INDEX, "Samsung Galaxy Nexus Android 4.1 (API 17)"); - setColumnWidth(myDeviceTable, SERIAL_COLUMN_INDEX, "0000-0000-00000"); - setColumnWidth(myDeviceTable, DEVICE_STATE_COLUMN_INDEX, "offline"); - setColumnWidth(myDeviceTable, COMPATIBILITY_COLUMN_INDEX, "yes"); - - // Do not recreate columns on every model update - this should help maintain the column sizes set above - myDeviceTable.setAutoCreateColumnsFromModel(false); - - // Allow sorting by columns (in lexicographic order) - myDeviceTable.setAutoCreateRowSorter(true); - - myRefreshingAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); - myBridge = AndroidSdkUtils.getDebugBridge(myFacet.getModule().getProject()); - } - - private static EnumSet getRequiredHardwareFeatures(List requiredFeatures) { - // Currently, this method is hardcoded to only search if the list of required features includes a watch. - // We may not want to search the device for every possible feature, but only a small subset of important - // features, starting with hardware type watch.. - - for (UsesFeature feature : requiredFeatures) { - AndroidAttributeValue name = feature.getName(); - if (name != null && UsesFeature.HARDWARE_TYPE_WATCH.equals(name.getStringValue())) { - return EnumSet.of(IDevice.HardwareFeature.WATCH); - } - } - - return EnumSet.noneOf(IDevice.HardwareFeature.class); - } - - private void setColumnWidth(JBTable deviceTable, int columnIndex, String sampleText) { - int width = getWidth(deviceTable, sampleText); - deviceTable.getColumnModel().getColumn(columnIndex).setPreferredWidth(width); - } - - private int getWidth(JBTable deviceTable, String sampleText) { - FontMetrics metrics = deviceTable.getFontMetrics(deviceTable.getFont()); - return metrics.stringWidth(sampleText); - } - - public void init(@Nullable String[] selectedSerials) { - previouslySelectedSerials = selectedSerials; - updateTable(); - addUpdatingRequest(); - } - - public void init(List selectedSerials) { - init(selectedSerials.stream().toArray(String[]::new)); - } - - private void updatePreviouslySelectedSerials() { - if (previouslySelectedSerials != null && !hadUserInteraction) { - resetSelection(previouslySelectedSerials); - } - } - - private final Runnable myUpdateRequest = new Runnable() { - @Override - public void run() { - updateTable(); - addUpdatingRequest(); - } - }; - - private void addUpdatingRequest() { - if (myRefreshingAlarm.isDisposed()) { - return; - } - myRefreshingAlarm.cancelAllRequests(); - myRefreshingAlarm.addRequest(myUpdateRequest, REFRESH_INTERVAL_MS); - } - - private void resetSelection(@NotNull String[] selectedSerials) { - MyDeviceTableModel model = (MyDeviceTableModel)myDeviceTable.getModel(); - Set selectedSerialsSet = new HashSet(); - Collections.addAll(selectedSerialsSet, selectedSerials); - IDevice[] myDevices = model.myDevices; - ListSelectionModel selectionModel = myDeviceTable.getSelectionModel(); - boolean cleared = false; - - for (int i = 0, n = myDevices.length; i < n; i++) { - String serialNumber = myDevices[i].getSerialNumber(); - if (selectedSerialsSet.contains(serialNumber)) { - if (!cleared) { - selectionModel.clearSelection(); - cleared = true; - } - selectionModel.addSelectionInterval(i, i); - } - } - } - - void updateTable() { - IDevice[] devices = myBridge != null ? getFilteredDevices(myBridge) : EMPTY_DEVICE_ARRAY; - if (devices.length > 1) { - // sort by API level - Arrays.sort(devices, new Comparator() { - @Override - public int compare(IDevice device1, IDevice device2) { - int apiLevel1 = safeGetApiLevel(device1); - int apiLevel2 = safeGetApiLevel(device2); - return apiLevel2 - apiLevel1; - } - - private int safeGetApiLevel(IDevice device) { - try { - String s = device.getProperty(IDevice.PROP_BUILD_API_LEVEL); - return StringUtil.isNotEmpty(s) ? Integer.parseInt(s) : 0; - } catch (Exception e) { - return 0; - } - } - }); - } - - if (!Arrays.equals(myDisplayedDevices, devices)) { - myDetectedDevicesRef.set(devices); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - refreshTable(); - } - }, ModalityState.stateForComponent(myDeviceTable)); - } - } - - private void refreshTable() { - IDevice[] devices = myDetectedDevicesRef.get(); - myDisplayedDevices = devices; - - final IDevice[] selectedDevices = getSelectedDevices(); - final TIntArrayList selectedRows = new TIntArrayList(); - for (int i = 0; i < devices.length; i++) { - if (ArrayUtil.indexOf(selectedDevices, devices[i]) >= 0) { - selectedRows.add(i); - } - } - - myProcessSelectionFlag = false; - myDeviceTable.setModel(new MyDeviceTableModel(devices)); - if (selectedRows.size() == 0 && devices.length > 0) { - myDeviceTable.getSelectionModel().setSelectionInterval(0, 0); - } - for (int selectedRow : selectedRows.toNativeArray()) { - if (selectedRow < devices.length) { - myDeviceTable.getSelectionModel().addSelectionInterval(selectedRow, selectedRow); - } - } - fireSelectedDevicesChanged(); - myProcessSelectionFlag = true; - updatePreviouslySelectedSerials(); - } - - public boolean hasDevices() { - return myDetectedDevicesRef.get().length > 0; - } - - public JComponent getPreferredFocusComponent() { - return myDeviceTable; - } - - @Nullable - public JComponent getPanel() { - return myPanel; - } - - @NotNull - public IDevice[] getSelectedDevices() { - int[] rows = mySelectedRows != null ? mySelectedRows : myDeviceTable.getSelectedRows(); - List result = new ArrayList(); - for (int row : rows) { - if (row >= 0) { - Object serial = myDeviceTable.getValueAt(row, SERIAL_COLUMN_INDEX); - final AndroidDebugBridge bridge = AndroidSdkUtils.getDebugBridge(myFacet.getModule().getProject()); - if (bridge == null) { - return EMPTY_DEVICE_ARRAY; - } - IDevice[] devices = getFilteredDevices(bridge); - for (IDevice device : devices) { - if (device.getSerialNumber().equals(serial.toString())) { - result.add(device); - break; - } - } - } - } - return result.toArray(new IDevice[result.size()]); - } - - @NotNull - private IDevice[] getFilteredDevices(AndroidDebugBridge bridge) { - final List filteredDevices = new ArrayList(); - for (IDevice device : bridge.getDevices()) { - if (myFilter == null || myFilter.value(device)) { - filteredDevices.add(device); - } - } - // Do not filter launching cloud devices as they are just unselectable progress markers - // that are replaced with the actual cloud devices as soon as they are up and the actual cloud devices will be filtered above. - return filteredDevices.toArray(new IDevice[filteredDevices.size()]); - } - - public void finish() { - mySelectedRows = myDeviceTable.getSelectedRows(); - } - - @Override - public void dispose() { - } - - public void setEnabled(boolean enabled) { - myDeviceTable.setEnabled(enabled); - } - - @NotNull - private static String getDeviceState(@NotNull IDevice device) { - IDevice.DeviceState state = device.getState(); - return state != null ? capitalize(state.name().toLowerCase()) : ""; - } - - public void fireSelectedDevicesChanged() { - for (DeviceChooserListener listener : myListeners) { - listener.selectedDevicesChanged(); - } - } - - public void addListener(@NotNull DeviceChooserListener listener) { - myListeners.add(listener); - } - - private class MyDeviceTableModel extends AbstractTableModel { - private final IDevice[] myDevices; - - public MyDeviceTableModel(IDevice[] devices) { - myDevices = devices; - } - - @Override - public String getColumnName(int column) { - return COLUMN_TITLES[column]; - } - - @Override - public int getRowCount() { - return myDevices.length; - } - - @Override - public int getColumnCount() { - return COLUMN_TITLES.length; - } - - @Override - @Nullable - public Object getValueAt(int rowIndex, int columnIndex) { - if (rowIndex >= myDevices.length) { - return null; - } - IDevice device = myDevices[rowIndex]; - switch (columnIndex) { - case DEVICE_NAME_COLUMN_INDEX: - return generateDeviceName(device); - case SERIAL_COLUMN_INDEX: - return device.getSerialNumber(); - case DEVICE_STATE_COLUMN_INDEX: - return getDeviceState(device); - case COMPATIBILITY_COLUMN_INDEX: - return new CanRunOnDeviceCompat(myFacet, device).get(); - } - return null; - } - - private String generateDeviceName(IDevice device) { - return device.getName() - .replace(device.getSerialNumber(), "") - .replaceAll("[-_]", " ") - .replaceAll("[\\[\\]]", ""); - } - - @Override - public Class getColumnClass(int columnIndex) { - if (columnIndex == COMPATIBILITY_COLUMN_INDEX) { - return LaunchCompatibility.class; - } else if (columnIndex == DEVICE_NAME_COLUMN_INDEX) { - return IDevice.class; - } else { - return String.class; - } - } - } - - private static class LaunchCompatibilityRenderer extends ColoredTableCellRenderer { - @Override - protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { - if (!(value instanceof LaunchCompatibility)) { - return; - } - - LaunchCompatibility compatibility = (LaunchCompatibility)value; - ThreeState compatible = compatibility.isCompatible(); - if (compatible == ThreeState.YES) { - append("Yes"); - } else { - if (compatible == ThreeState.NO) { - append("No", SimpleTextAttributes.ERROR_ATTRIBUTES); - } else { - append("Maybe"); - } - String reason = compatibility.getReason(); - if (reason != null) { - append(", "); - append(reason); - } - } - } - } -} diff --git a/src/main/java/com/developerphil/adbidea/ui/NotificationHelper.java b/src/main/java/com/developerphil/adbidea/ui/NotificationHelper.java deleted file mode 100644 index 3f5f6a0..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/NotificationHelper.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.developerphil.adbidea.ui; - -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationGroup; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; - -public class NotificationHelper { - - public static final NotificationGroup INFO = NotificationGroup.logOnlyGroup("ADB Idea (Logging)"); - public static final NotificationGroup ERRORS = NotificationGroup.balloonGroup("ADB Idea (Errors)"); - public static final NotificationListener NOOP_LISTENER = (notification, event) -> { - }; - - public static void info(String message) { - sendNotification(message, NotificationType.INFORMATION, INFO); - } - - public static void error(String message) { - sendNotification(message, NotificationType.ERROR, ERRORS); - } - - private static void sendNotification(String message, NotificationType notificationType, NotificationGroup notificationGroup) { - Notification adb_idea = notificationGroup.createNotification("ADB IDEA", escapeString(message), notificationType, NOOP_LISTENER); - adb_idea.notify(null); - } - - - private static String escapeString(String string) { - return string.replaceAll("\n", "\n
"); - } -} diff --git a/src/main/java/com/developerphil/adbidea/ui/Utils.java b/src/main/java/com/developerphil/adbidea/ui/Utils.java deleted file mode 100644 index 222873d..0000000 --- a/src/main/java/com/developerphil/adbidea/ui/Utils.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.developerphil.adbidea.ui; - -import java.awt.Color; -import javax.swing.JTextPane; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.MutableAttributeSet; -import javax.swing.text.SimpleAttributeSet; -import javax.swing.text.StyleConstants; - -/** - * Created by XQ Yang on 2018/6/25 18:14. - * Description : - */ - -public class Utils { - public Utils() { - } - - public static boolean isEmpty(CharSequence str) { - return str == null || str.length() == 0; - } - - - public static synchronized void append2TextPane(String str, Color color, JTextPane textPane) { - Document doc = textPane.getDocument(); - if (doc != null) { - try { - MutableAttributeSet attr = null; - if (color != null) { - attr = new SimpleAttributeSet(); - StyleConstants.setForeground(attr, color); - StyleConstants.setBold(attr, true); - } - doc.insertString(doc.getLength(), str, attr); - } catch (BadLocationException e) { - } - } - } - - public static void append2TextPane(String str, JTextPane textPane) { - append2TextPane(str, null, textPane); - } - - public static void append2TextPaneNewLine(String str, Color color, JTextPane textPane) { - append2TextPane(str+"\n", color, textPane); - } - public static void append2TextPaneNewLine(String str, JTextPane textPane) { - append2TextPane(str+"\n", null, textPane); - } -} diff --git a/src/main/java/org/joor/Reflect.java b/src/main/java/org/joor/Reflect.java deleted file mode 100644 index d23c8d2..0000000 --- a/src/main/java/org/joor/Reflect.java +++ /dev/null @@ -1,812 +0,0 @@ -/** - * Copyright (c) 2011-2013, Lukas Eder, lukas.eder@gmail.com - * All rights reserved. - * - * This software is licensed to you under the Apache License, Version 2.0 - * (the "License"); You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * . Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * . Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * . Neither the name "jOOR" nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.joor; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * A wrapper for an {@link Object} or {@link Class} upon which reflective calls - * can be made. - *

- * An example of using Reflect is

- * // Static import all reflection methods to decrease verbosity
- * import static org.joor.Reflect.*;
- *
- * // Wrap an Object / Class / class name with the on() method:
- * on("java.lang.String")
- * // Invoke constructors using the create() method:
- * .create("Hello World")
- * // Invoke methods using the call() method:
- * .call("toString")
- * // Retrieve the wrapped object
- *
- * @author Lukas Eder
- * @author Irek Matysiewicz
- */
-public class Reflect {
-
-    // ---------------------------------------------------------------------
-    // Static API used as entrance points to the fluent API
-    // ---------------------------------------------------------------------
-
-    /**
-     * Wrap a class name.
-     * 

- * This is the same as calling on(Class.forName(name)) - * - * @param name A fully qualified class name - * @return A wrapped class object, to be used for further reflection. - * @throws ReflectException If any reflection exception occurred. - * @see #on(Class) - */ - public static Reflect on(String name) throws ReflectException { - return on(forName(name)); - } - - /** - * Wrap a class name, loading it via a given class loader. - *

- * This is the same as calling - * on(Class.forName(name, classLoader)) - * - * @param name A fully qualified class name. - * @param classLoader The class loader in whose context the class should be - * loaded. - * @return A wrapped class object, to be used for further reflection. - * @throws ReflectException If any reflection exception occurred. - * @see #on(Class) - */ - public static Reflect on(String name, ClassLoader classLoader) throws ReflectException { - return on(forName(name, classLoader)); - } - - /** - * Wrap a class. - *

- * Use this when you want to access static fields and methods on a - * {@link Class} object, or as a basis for constructing objects of that - * class using {@link #create(Object...)} - * - * @param clazz The class to be wrapped - * @return A wrapped class object, to be used for further reflection. - */ - public static Reflect on(Class clazz) { - return new Reflect(clazz); - } - - /** - * Wrap an object. - *

- * Use this when you want to access instance fields and methods on any - * {@link Object} - * - * @param object The object to be wrapped - * @return A wrapped object, to be used for further reflection. - */ - public static Reflect on(Object object) { - return new Reflect(object); - } - - /** - * Conveniently render an {@link AccessibleObject} accessible. - *

- * To prevent {@link SecurityException}, this is only done if the argument - * object and its declaring class are non-public. - * - * @param accessible The object to render accessible - * @return The argument object rendered accessible - */ - public static T accessible(T accessible) { - if (accessible == null) { - return null; - } - - if (accessible instanceof Member) { - Member member = (Member) accessible; - - if (Modifier.isPublic(member.getModifiers()) && - Modifier.isPublic(member.getDeclaringClass().getModifiers())) { - - return accessible; - } - } - - // [jOOQ #3392] The accessible flag is set to false by default, also for public members. - if (!accessible.isAccessible()) { - accessible.setAccessible(true); - } - - return accessible; - } - - // --------------------------------------------------------------------- - // Members - // --------------------------------------------------------------------- - - /** - * The wrapped object - */ - private final Object object; - - /** - * A flag indicating whether the wrapped object is a {@link Class} (for - * accessing static fields and methods), or any other type of {@link Object} - * (for accessing instance fields and methods). - */ - private final boolean isClass; - - // --------------------------------------------------------------------- - // Constructors - // --------------------------------------------------------------------- - - private Reflect(Class type) { - this.object = type; - this.isClass = true; - } - - private Reflect(Object object) { - this.object = object; - this.isClass = false; - } - - // --------------------------------------------------------------------- - // Fluent Reflection API - // --------------------------------------------------------------------- - - /** - * Get the wrapped object - * - * @param A convenience generic parameter for automatic unsafe casting - */ - @SuppressWarnings("unchecked") - public T get() { - return (T) object; - } - - /** - * Set a field value. - *

- * This is roughly equivalent to {@link Field#set(Object, Object)}. If the - * wrapped object is a {@link Class}, then this will set a value to a static - * member field. If the wrapped object is any other {@link Object}, then - * this will set a value to an instance member field. - * - * @param name The field name - * @param value The new field value - * @return The same wrapped object, to be used for further reflection. - * @throws ReflectException If any reflection exception occurred. - */ - public Reflect set(String name, Object value) throws ReflectException { - try { - Field field = field0(name); - field.set(object, unwrap(value)); - return this; - } - catch (Exception e) { - throw new ReflectException(e); - } - } - - /** - * Get a field value. - *

- * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped - * object is a {@link Class}, then this will get a value from a static - * member field. If the wrapped object is any other {@link Object}, then - * this will get a value from an instance member field. - *

- * If you want to "navigate" to a wrapped version of the field, use - * {@link #field(String)} instead. - * - * @param name The field name - * @return The field value - * @throws ReflectException If any reflection exception occurred. - * @see #field(String) - */ - public T get(String name) throws ReflectException { - return field(name).get(); - } - - /** - * Get a wrapped field. - *

- * This is roughly equivalent to {@link Field#get(Object)}. If the wrapped - * object is a {@link Class}, then this will wrap a static member field. If - * the wrapped object is any other {@link Object}, then this wrap an - * instance member field. - * - * @param name The field name - * @return The wrapped field - * @throws ReflectException If any reflection exception occurred. - */ - public Reflect field(String name) throws ReflectException { - try { - Field field = field0(name); - return on(field.get(object)); - } - catch (Exception e) { - throw new ReflectException(e); - } - } - - private Field field0(String name) throws ReflectException { - Class type = type(); - - // Try getting a public field - try { - return type.getField(name); - } - - // Try again, getting a non-public field - catch (NoSuchFieldException e) { - do { - try { - return accessible(type.getDeclaredField(name)); - } - catch (NoSuchFieldException ignore) {} - - type = type.getSuperclass(); - } - while (type != null); - - throw new ReflectException(e); - } - } - - /** - * Get a Map containing field names and wrapped values for the fields' - * values. - *

- * If the wrapped object is a {@link Class}, then this will return static - * fields. If the wrapped object is any other {@link Object}, then this will - * return instance fields. - *

- * These two calls are equivalent

-     * on(object).field("myField");
-     * on(object).fields().get("myField");
-     * 
- * - * @return A map containing field names and wrapped values. - */ - public Map fields() { - Map result = new LinkedHashMap(); - Class type = type(); - - do { - for (Field field : type.getDeclaredFields()) { - if (!isClass ^ Modifier.isStatic(field.getModifiers())) { - String name = field.getName(); - - if (!result.containsKey(name)) - result.put(name, field(name)); - } - } - - type = type.getSuperclass(); - } - while (type != null); - - return result; - } - - /** - * Call a method by its name. - *

- * This is a convenience method for calling - * call(name, new Object[0]) - * - * @param name The method name - * @return The wrapped method result or the same wrapped object if the - * method returns void, to be used for further - * reflection. - * @throws ReflectException If any reflection exception occurred. - * @see #call(String, Object...) - */ - public Reflect call(String name) throws ReflectException { - return call(name, new Object[0]); - } - - /** - * Call a method by its name. - *

- * This is roughly equivalent to {@link Method#invoke(Object, Object...)}. - * If the wrapped object is a {@link Class}, then this will invoke a static - * method. If the wrapped object is any other {@link Object}, then this will - * invoke an instance method. - *

- * Just like {@link Method#invoke(Object, Object...)}, this will try to wrap - * primitive types or unwrap primitive type wrappers if applicable. If - * several methods are applicable, by that rule, the first one encountered - * is called. i.e. when calling

-     * on(...).call("method", 1, 1);
-     * 
The first of the following methods will be called: - *
-     * public void method(int param1, Integer param2);
-     * public void method(Integer param1, int param2);
-     * public void method(Number param1, Number param2);
-     * public void method(Number param1, Object param2);
-     * public void method(int param1, Object param2);
-     * 
- *

- * The best matching method is searched for with the following strategy: - *

    - *
  1. public method with exact signature match in class hierarchy
  2. - *
  3. non-public method with exact signature match on declaring class
  4. - *
  5. public method with similar signature in class hierarchy
  6. - *
  7. non-public method with similar signature on declaring class
  8. - *
- * - * @param name The method name - * @param args The method arguments - * @return The wrapped method result or the same wrapped object if the - * method returns void, to be used for further - * reflection. - * @throws ReflectException If any reflection exception occurred. - */ - public Reflect call(String name, Object... args) throws ReflectException { - Class[] types = types(args); - - // Try invoking the "canonical" method, i.e. the one with exact - // matching argument types - try { - Method method = exactMethod(name, types); - return on(method, object, args); - } - - // If there is no exact match, try to find a method that has a "similar" - // signature if primitive argument types are converted to their wrappers - catch (NoSuchMethodException e) { - try { - Method method = similarMethod(name, types); - return on(method, object, args); - } catch (NoSuchMethodException e1) { - throw new ReflectException(e1); - } - } - } - - /** - * Searches a method with the exact same signature as desired. - *

- * If a public method is found in the class hierarchy, this method is returned. - * Otherwise a private method with the exact same signature is returned. - * If no exact match could be found, we let the {@code NoSuchMethodException} pass through. - */ - private Method exactMethod(String name, Class[] types) throws NoSuchMethodException { - Class type = type(); - - // first priority: find a public method with exact signature match in class hierarchy - try { - return type.getMethod(name, types); - } - - // second priority: find a private method with exact signature match on declaring class - catch (NoSuchMethodException e) { - do { - try { - return type.getDeclaredMethod(name, types); - } - catch (NoSuchMethodException ignore) {} - - type = type.getSuperclass(); - } - while (type != null); - - throw new NoSuchMethodException(); - } - } - - /** - * Searches a method with a similar signature as desired using - * {@link #isSimilarSignature(java.lang.reflect.Method, String, Class[])}. - *

- * First public methods are searched in the class hierarchy, then private - * methods on the declaring class. If a method could be found, it is - * returned, otherwise a {@code NoSuchMethodException} is thrown. - */ - private Method similarMethod(String name, Class[] types) throws NoSuchMethodException { - Class type = type(); - - // first priority: find a public method with a "similar" signature in class hierarchy - // similar interpreted in when primitive argument types are converted to their wrappers - for (Method method : type.getMethods()) { - if (isSimilarSignature(method, name, types)) { - return method; - } - } - - // second priority: find a non-public method with a "similar" signature on declaring class - do { - for (Method method : type.getDeclaredMethods()) { - if (isSimilarSignature(method, name, types)) { - return method; - } - } - - type = type.getSuperclass(); - } - while (type != null); - - throw new NoSuchMethodException("No similar method " + name + " with params " + Arrays.toString(types) + " could be found on type " + type() + "."); - } - - /** - * Determines if a method has a "similar" signature, especially if wrapping - * primitive argument types would result in an exactly matching signature. - */ - private boolean isSimilarSignature(Method possiblyMatchingMethod, String desiredMethodName, Class[] desiredParamTypes) { - return possiblyMatchingMethod.getName().equals(desiredMethodName) && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes); - } - - /** - * Call a constructor. - *

- * This is a convenience method for calling - * create(new Object[0]) - * - * @return The wrapped new object, to be used for further reflection. - * @throws ReflectException If any reflection exception occurred. - * @see #create(Object...) - */ - public Reflect create() throws ReflectException { - return create(new Object[0]); - } - - /** - * Call a constructor. - *

- * This is roughly equivalent to {@link Constructor#newInstance(Object...)}. - * If the wrapped object is a {@link Class}, then this will create a new - * object of that class. If the wrapped object is any other {@link Object}, - * then this will create a new object of the same type. - *

- * Just like {@link Constructor#newInstance(Object...)}, this will try to - * wrap primitive types or unwrap primitive type wrappers if applicable. If - * several constructors are applicable, by that rule, the first one - * encountered is called. i.e. when calling

-     * on(C.class).create(1, 1);
-     * 
The first of the following constructors will be applied: - *
-     * public C(int param1, Integer param2);
-     * public C(Integer param1, int param2);
-     * public C(Number param1, Number param2);
-     * public C(Number param1, Object param2);
-     * public C(int param1, Object param2);
-     * 
- * - * @param args The constructor arguments - * @return The wrapped new object, to be used for further reflection. - * @throws ReflectException If any reflection exception occurred. - */ - public Reflect create(Object... args) throws ReflectException { - Class[] types = types(args); - - // Try invoking the "canonical" constructor, i.e. the one with exact - // matching argument types - try { - Constructor constructor = type().getDeclaredConstructor(types); - return on(constructor, args); - } - - // If there is no exact match, try to find one that has a "similar" - // signature if primitive argument types are converted to their wrappers - catch (NoSuchMethodException e) { - for (Constructor constructor : type().getDeclaredConstructors()) { - if (match(constructor.getParameterTypes(), types)) { - return on(constructor, args); - } - } - - throw new ReflectException(e); - } - } - - /** - * Create a proxy for the wrapped object allowing to typesafely invoke - * methods on it using a custom interface - * - * @param proxyType The interface type that is implemented by the proxy - * @return A proxy for the wrapped object - */ - @SuppressWarnings("unchecked") - public

P as(Class

proxyType) { - final boolean isMap = (object instanceof Map); - final InvocationHandler handler = new InvocationHandler() { - @SuppressWarnings("null") - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - String name = method.getName(); - - // Actual method name matches always come first - try { - return on(object).call(name, args).get(); - } - - // [#14] Emulate POJO behaviour on wrapped map objects - catch (ReflectException e) { - if (isMap) { - Map map = (Map) object; - int length = (args == null ? 0 : args.length); - - if (length == 0 && name.startsWith("get")) { - return map.get(property(name.substring(3))); - } - else if (length == 0 && name.startsWith("is")) { - return map.get(property(name.substring(2))); - } - else if (length == 1 && name.startsWith("set")) { - map.put(property(name.substring(3)), args[0]); - return null; - } - } - - throw e; - } - } - }; - - return (P) Proxy.newProxyInstance(proxyType.getClassLoader(), new Class[] { proxyType }, handler); - } - - /** - * Get the POJO property name of an getter/setter - */ - private static String property(String string) { - int length = string.length(); - - if (length == 0) { - return ""; - } - else if (length == 1) { - return string.toLowerCase(); - } - else { - return string.substring(0, 1).toLowerCase() + string.substring(1); - } - } - - // --------------------------------------------------------------------- - // Object API - // --------------------------------------------------------------------- - - /** - * Check whether two arrays of types match, converting primitive types to - * their corresponding wrappers. - */ - private boolean match(Class[] declaredTypes, Class[] actualTypes) { - if (declaredTypes.length == actualTypes.length) { - for (int i = 0; i < actualTypes.length; i++) { - if (actualTypes[i] == NULL.class) - continue; - - if (wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) - continue; - - return false; - } - - return true; - } - else { - return false; - } - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return object.hashCode(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof Reflect) { - return object.equals(((Reflect) obj).get()); - } - - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return object.toString(); - } - - // --------------------------------------------------------------------- - // Utility methods - // --------------------------------------------------------------------- - - /** - * Wrap an object created from a constructor - */ - private static Reflect on(Constructor constructor, Object... args) throws ReflectException { - try { - return on(accessible(constructor).newInstance(args)); - } - catch (Exception e) { - throw new ReflectException(e); - } - } - - /** - * Wrap an object returned from a method - */ - private static Reflect on(Method method, Object object, Object... args) throws ReflectException { - try { - accessible(method); - - if (method.getReturnType() == void.class) { - method.invoke(object, args); - return on(object); - } - else { - return on(method.invoke(object, args)); - } - } - catch (Exception e) { - throw new ReflectException(e); - } - } - - /** - * Unwrap an object - */ - private static Object unwrap(Object object) { - if (object instanceof Reflect) { - return ((Reflect) object).get(); - } - - return object; - } - - /** - * Get an array of types for an array of objects - * - * @see Object#getClass() - */ - private static Class[] types(Object... values) { - if (values == null) { - return new Class[0]; - } - - Class[] result = new Class[values.length]; - - for (int i = 0; i < values.length; i++) { - Object value = values[i]; - result[i] = value == null ? NULL.class : value.getClass(); - } - - return result; - } - - /** - * Load a class - * - * @see Class#forName(String) - */ - private static Class forName(String name) throws ReflectException { - try { - return Class.forName(name); - } - catch (Exception e) { - throw new ReflectException(e); - } - } - - private static Class forName(String name, ClassLoader classLoader) throws ReflectException { - try { - return Class.forName(name, true, classLoader); - } - catch (Exception e) { - throw new ReflectException(e); - } - } - - /** - * Get the type of the wrapped object. - * - * @see Object#getClass() - */ - public Class type() { - if (isClass) { - return (Class) object; - } - else { - return object.getClass(); - } - } - - /** - * Get a wrapper type for a primitive type, or the argument type itself, if - * it is not a primitive type. - */ - public static Class wrapper(Class type) { - if (type == null) { - return null; - } - else if (type.isPrimitive()) { - if (boolean.class == type) { - return Boolean.class; - } - else if (int.class == type) { - return Integer.class; - } - else if (long.class == type) { - return Long.class; - } - else if (short.class == type) { - return Short.class; - } - else if (byte.class == type) { - return Byte.class; - } - else if (double.class == type) { - return Double.class; - } - else if (float.class == type) { - return Float.class; - } - else if (char.class == type) { - return Character.class; - } - else if (void.class == type) { - return Void.class; - } - } - - return type; - } - - private static class NULL {} -} \ No newline at end of file diff --git a/src/main/java/org/joor/ReflectException.java b/src/main/java/org/joor/ReflectException.java deleted file mode 100644 index ae142cb..0000000 --- a/src/main/java/org/joor/ReflectException.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2011-2013, Lukas Eder, lukas.eder@gmail.com - * All rights reserved. - * - * This software is licensed to you under the Apache License, Version 2.0 - * (the "License"); You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * . Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * . Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * . Neither the name "jOOR" nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.joor; - -import java.lang.reflect.InvocationTargetException; - -/** - * A unchecked wrapper for any of Java's checked reflection exceptions: - *

- * These exceptions are - *

    - *
  • {@link ClassNotFoundException}
  • - *
  • {@link IllegalAccessException}
  • - *
  • {@link IllegalArgumentException}
  • - *
  • {@link InstantiationException}
  • - *
  • {@link InvocationTargetException}
  • - *
  • {@link NoSuchMethodException}
  • - *
  • {@link NoSuchFieldException}
  • - *
  • {@link SecurityException}
  • - *
- * - * @author Lukas Eder - */ -public class ReflectException extends RuntimeException { - - /** - * Generated UID - */ - private static final long serialVersionUID = -6213149635297151442L; - - public ReflectException(String message) { - super(message); - } - - public ReflectException(String message, Throwable cause) { - super(message, cause); - } - - public ReflectException() { - super(); - } - - public ReflectException(Throwable cause) { - super(cause); - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/ObjectGraph.kt b/src/main/kotlin/com/developerphil/adbidea/ObjectGraph.kt index 77be3b3..d9482bc 100644 --- a/src/main/kotlin/com/developerphil/adbidea/ObjectGraph.kt +++ b/src/main/kotlin/com/developerphil/adbidea/ObjectGraph.kt @@ -1,27 +1,29 @@ package com.developerphil.adbidea -import com.developerphil.adbidea.dagger.DaggerPluginComponent -import com.developerphil.adbidea.dagger.PluginComponent -import com.developerphil.adbidea.dagger.PluginModule +import com.developerphil.adbidea.accessor.preference.ProjectPreferenceAccessor +import com.developerphil.adbidea.adb.BridgeImpl +import com.developerphil.adbidea.adb.DeviceResultFetcher +import com.developerphil.adbidea.adb.UseSameDevicesHelper import com.intellij.openapi.components.ProjectComponent import com.intellij.openapi.project.Project -class ObjectGraph(project: Project) : ProjectComponent, - PluginComponent by DaggerPluginComponent.builder().pluginModule(PluginModule(project)).build() { +// This is more of a service locator than a proper DI framework. +// It's not used often enough in the codebase to warrant the complexity of a DI solution like dagger. +class ObjectGraph(private val project: Project) : ProjectComponent { - override fun projectOpened() { - } + val deviceResultFetcher by lazy { DeviceResultFetcher(project, useSameDevicesHelper, bridge) } + val pluginPreferences: PluginPreferences by lazy { PluginPreferencesImpl(preferenceAccessor) } - override fun projectClosed() { - } + private val useSameDevicesHelper by lazy { UseSameDevicesHelper(pluginPreferences, bridge) } + private val preferenceAccessor by lazy { ProjectPreferenceAccessor(project) } + private val bridge by lazy { BridgeImpl(project) } - override fun initComponent() { - } - override fun disposeComponent() { - } + // Project Component Boilerplate + override fun projectOpened() = Unit - override fun getComponentName(): String { - return "DaggerObjectGraph" - } + override fun projectClosed() = Unit + override fun initComponent() = Unit + override fun disposeComponent() = Unit + override fun getComponentName(): String = "InjectionObjectGraph" } diff --git a/src/main/java/org/joor/ReflectKt.kt b/src/main/kotlin/com/developerphil/adbidea/ReflectKt.kt similarity index 67% rename from src/main/java/org/joor/ReflectKt.kt rename to src/main/kotlin/com/developerphil/adbidea/ReflectKt.kt index a19110d..de7e7cf 100644 --- a/src/main/java/org/joor/ReflectKt.kt +++ b/src/main/kotlin/com/developerphil/adbidea/ReflectKt.kt @@ -1,4 +1,6 @@ -package org.joor +package com.developerphil.adbidea + +import org.joor.Reflect inline fun on() = Reflect.on(T::class.java) inline fun Reflect.asType() = this.`as`(T::class.java) \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/AdbAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/AdbAction.kt new file mode 100644 index 0000000..ada2616 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/AdbAction.kt @@ -0,0 +1,11 @@ +package com.developerphil.adbidea.action + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.PlatformDataKeys +import com.intellij.openapi.project.Project + +abstract class AdbAction : AnAction() { + override fun actionPerformed(event: AnActionEvent) = actionPerformed(event, event.getData(PlatformDataKeys.PROJECT)!!) + abstract fun actionPerformed(e: AnActionEvent, project: Project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/ClearDataAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/ClearDataAction.kt new file mode 100644 index 0000000..97bced3 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/ClearDataAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class ClearDataAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.clearData(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/ClearDataAndRestartAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/ClearDataAndRestartAction.kt new file mode 100644 index 0000000..9dc721c --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/ClearDataAndRestartAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class ClearDataAndRestartAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.clearDataAndRestart(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/GrantPermissionsAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/GrantPermissionsAction.kt new file mode 100644 index 0000000..72931ed --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/GrantPermissionsAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class GrantPermissionsAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.grantPermissions(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/KillAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/KillAction.kt new file mode 100644 index 0000000..507bba6 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/KillAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class KillAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.kill(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/QuickListAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/QuickListAction.kt new file mode 100644 index 0000000..aa6b52f --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/QuickListAction.kt @@ -0,0 +1,45 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbUtil +import com.intellij.ide.actions.QuickSwitchSchemeAction +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.Project + +class QuickListAction : QuickSwitchSchemeAction(), DumbAware { + override fun fillActions(project: Project?, group: DefaultActionGroup, dataContext: DataContext) { + + if (project == null) { + return + } + + addAction("com.developerphil.adbidea.action.UninstallAction", group) + addAction("com.developerphil.adbidea.action.KillAction", group) + addAction("com.developerphil.adbidea.action.StartAction", group) + addAction("com.developerphil.adbidea.action.RestartAction", group) + addAction("com.developerphil.adbidea.action.ClearDataAction", group) + addAction("com.developerphil.adbidea.action.ClearDataAndRestartAction", group) + addAction("com.developerphil.adbidea.action.RevokePermissionsAction", group) + if (AdbUtil.isDebuggingAvailable) { + group.addSeparator() + addAction("com.developerphil.adbidea.action.StartWithDebuggerAction", group) + addAction("com.developerphil.adbidea.action.RestartWithDebuggerAction", group) + } + group.addSeparator() + addAction("com.developerphil.adbidea.action.QuickListSupplementaryAction", group) + } + + + private fun addAction(actionId: String, toGroup: DefaultActionGroup) { + // add action to group if it is available + ActionManager.getInstance().getAction(actionId)?.let { + toGroup.add(it) + } + } + + override fun isEnabled() = true + override fun getPopupTitle(e: AnActionEvent) = "ADB Operations Popup" +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/QuickListSupplementaryAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/QuickListSupplementaryAction.kt new file mode 100644 index 0000000..b8d571e --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/QuickListSupplementaryAction.kt @@ -0,0 +1,45 @@ +package com.developerphil.adbidea.action + +import com.intellij.ide.actions.QuickSwitchSchemeAction +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.Project + +class QuickListSupplementaryAction : QuickSwitchSchemeAction(), DumbAware { + override fun fillActions(project: Project?, + group: DefaultActionGroup, + dataContext: DataContext) { + + if (project == null) { + return + } + addAction("com.developerphil.adbidea.action.extend.ApplicationManagementPopupAction", group) + addAction("com.developerphil.adbidea.action.extend.InteractingAction", group) + addAction("com.developerphil.adbidea.action.extend.ShowDeviceInfoAction", group) + addAction("com.developerphil.adbidea.action.extend.InstallApkAction", group) + addAction("com.developerphil.adbidea.action.extend.PutStringAction", group) + addAction("com.developerphil.adbidea.action.extend.ScreenRecordAction", group) + addAction("com.developerphil.adbidea.action.extend.ScreenCaptureAction", group) + } + + override fun isEnabled(): Boolean { + return true + } + + private fun addAction(actionId: String, toGroup: DefaultActionGroup) { + val action = ActionManager.getInstance().getAction(actionId) + + // add action to group if it is available + if (action != null) { + toGroup.add(action) + } + } + + override fun getPopupTitle(e: AnActionEvent): String { + return "ADB Supplementary Operations Popup" + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/action/RestartAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/RestartAction.kt new file mode 100644 index 0000000..cc86686 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/RestartAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class RestartAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.restartDefaultActivity(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/RestartWithDebuggerAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/RestartWithDebuggerAction.kt new file mode 100644 index 0000000..7c274a5 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/RestartWithDebuggerAction.kt @@ -0,0 +1,14 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.developerphil.adbidea.adb.AdbUtil +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class RestartWithDebuggerAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.restartDefaultActivityWithDebugger(project) + + override fun update(e: AnActionEvent) { + e.presentation.isEnabled = AdbUtil.isDebuggingAvailable + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/RevokePermissionsAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/RevokePermissionsAction.kt new file mode 100644 index 0000000..15f553e --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/RevokePermissionsAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class RevokePermissionsAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.revokePermissions(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/RevokePermissionsAndRestartAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/RevokePermissionsAndRestartAction.kt new file mode 100644 index 0000000..b6a7c56 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/RevokePermissionsAndRestartAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class RevokePermissionsAndRestartAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.revokePermissionsAndRestart(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/StartAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/StartAction.kt new file mode 100644 index 0000000..4fd6ec1 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/StartAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class StartAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.startDefaultActivity(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/StartWithDebuggerAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/StartWithDebuggerAction.kt new file mode 100644 index 0000000..bbc5cce --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/StartWithDebuggerAction.kt @@ -0,0 +1,14 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.developerphil.adbidea.adb.AdbUtil +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class StartWithDebuggerAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.startDefaultActivityWithDebugger(project) + + override fun update(e: AnActionEvent) { + e.presentation.isEnabled = AdbUtil.isDebuggingAvailable + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/UninstallAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/UninstallAction.kt new file mode 100644 index 0000000..baccdec --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/UninstallAction.kt @@ -0,0 +1,9 @@ +package com.developerphil.adbidea.action + +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +class UninstallAction : AdbAction() { + override fun actionPerformed(e: AnActionEvent, project: Project) = AdbFacade.uninstall(project) +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt index 5febff7..830be95 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt @@ -10,13 +10,12 @@ import com.intellij.openapi.project.Project * Description : */ class ApplicationManagementPopupAction:AdbAction(){ - - - override fun actionPerformed(e: AnActionEvent?, project: Project?) { + override fun actionPerformed(e: AnActionEvent, project: Project) { val form = ApplicationManagementFrame(project) form.pack() form.isVisible = true } + } \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt index 12a3f10..8fcd9f4 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt @@ -15,11 +15,7 @@ import java.io.File * Description : */ class InstallApkAction : AdbAction() { - - - private lateinit var apkChooserDescriptor: FileChooserDescriptor - - override fun actionPerformed(e: AnActionEvent?, project: Project?) { + override fun actionPerformed(e: AnActionEvent, project: Project) { // Set 'chooseFolders' depend on OS, because macOS application represents a directory. apkChooserDescriptor = FileChooserDescriptor(true, OS.isMacOSX(), false, false, false, true) apkChooserDescriptor.title = "selected apk file to install,support multiple choose" @@ -32,4 +28,8 @@ class InstallApkAction : AdbAction() { AdbFacade.installApk(project, apks.map { File(it.canonicalPath) }.toList()) } } + + + private lateinit var apkChooserDescriptor: FileChooserDescriptor + } \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt index f25427e..70a45bc 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt @@ -13,7 +13,7 @@ import com.intellij.openapi.project.Project class InteractingAction : AdbAction() { - override fun actionPerformed(e: AnActionEvent?, project: Project?) { + override fun actionPerformed(e: AnActionEvent, project: Project) { val form = InteractingFrame(project) form.pack() form.isVisible = true diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt index 8fd6a32..9b76998 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt @@ -14,8 +14,8 @@ import javax.swing.JOptionPane class PutStringAction : AdbAction() { - override fun actionPerformed(e: AnActionEvent?, project: Project?) { - var result = JOptionPane.showInputDialog("Input simple string(ASCII) put to device") + override fun actionPerformed(e: AnActionEvent, project: Project) { + var result = JOptionPane.showInputDialog("Input simple string(ASCII) put to device,need open USB debugging(Security settings)") if (!result.isNullOrEmpty()) { result = result.replace(" ","") AdbFacade.putStringToDevice(project,result) diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt index 3658790..a13dd65 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt @@ -23,7 +23,7 @@ class ScreenCaptureAction : AdbAction() { } - override fun actionPerformed(e: AnActionEvent?, project: Project?) { + override fun actionPerformed(e: AnActionEvent, project: Project) { AdbFacade.getDeviceModel(project){model:String-> deviceName = model } diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt index 912101a..13edec0 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt @@ -27,14 +27,14 @@ class ScreenRecordAction : AdbAction() { val videoName: String by lazy { "${deviceName}_${dateFormat.format(Date())}.mp4" } val remotePath: String by lazy { "/sdcard/$videoName" } var getFiled = false - override fun actionPerformed(e: AnActionEvent?, project: Project) { + override fun actionPerformed(e: AnActionEvent, project: Project) { AdbFacade.getDeviceModel(project) { model: String -> deviceName = model } val dialog = RecordOptionDialog { deleteRemoteFile -> saveDirChooserDescriptor.title = "Select $videoName save to..." val choose = FileChooserDialogImpl(saveDirChooserDescriptor, project) - .choose(project, selectedFile) + .choose(project, selectedFile) if (choose.isNotEmpty()) { selectedFile = choose[0] AdbFacade.pullFile(project, remotePath, File(selectedFile?.canonicalPath, videoName), deleteRemoteFile) diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/SetDefaultModuleAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/SetDefaultModuleAction.kt index 499b92d..84eb7c9 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/SetDefaultModuleAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/SetDefaultModuleAction.kt @@ -1,10 +1,10 @@ package com.developerphil.adbidea.action.extend import com.developerphil.adbidea.action.AdbAction -import com.developerphil.adbidea.adb.DeviceResultFetcher import com.developerphil.adbidea.ui.ModuleChooserDialogHelper import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.Project +import org.jetbrains.android.util.AndroidUtils /** * @describe @@ -13,10 +13,11 @@ import com.intellij.openapi.project.Project */ class SetDefaultModuleAction : AdbAction() { - override fun actionPerformed(e: AnActionEvent?, project: Project?) { + override fun actionPerformed(e: AnActionEvent, project: Project) { project?.let { - val facets = DeviceResultFetcher.getApplicationFacets(it) + val facets = AndroidUtils.getApplicationFacets(project) ModuleChooserDialogHelper.showDialogForFacets(project, facets, true) } } + } \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt index 832d6a9..8d1e3e1 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt @@ -13,7 +13,7 @@ import com.intellij.openapi.project.Project class ShowDeviceInfoAction : AdbAction() { - override fun actionPerformed(e: AnActionEvent?, project: Project?) { + override fun actionPerformed(e: AnActionEvent, project: Project) { val form = DeviceInfoFrame(project) form.pack() form.isVisible = true diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/AdbFacade.kt b/src/main/kotlin/com/developerphil/adbidea/adb/AdbFacade.kt new file mode 100644 index 0000000..d097e12 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/AdbFacade.kt @@ -0,0 +1,158 @@ +package com.developerphil.adbidea.adb + +import com.developerphil.adbidea.ObjectGraph +import com.developerphil.adbidea.adb.command.* +import com.developerphil.adbidea.bean.BoundItemBean +import com.developerphil.adbidea.ui.NotificationHelper +import com.google.common.util.concurrent.ThreadFactoryBuilder +import com.intellij.openapi.project.Project +import java.io.File +import java.util.concurrent.Executors + +object AdbFacade { + private val EXECUTOR = Executors.newCachedThreadPool(ThreadFactoryBuilder().setNameFormat("AdbIdea-%d").build()) + + + + fun uninstall(project: Project, packageName: String) { + executeOnDevice(project, UninstallCommand(packageName)) + } + + fun uninstall(project: Project) { + executeOnDevice(project, UninstallCommand()) + } + + fun installApk(project: Project, apks: List) { + executeOnDevice(project, InstallApkCommand(apks)) + } + + fun kill(project: Project) { + executeOnDevice(project, KillCommand()) + } + + fun grantPermissions(project: Project) { + executeOnDevice(project, GrantPermissionsCommand()) + } + + fun revokePermissions(project: Project) { + executeOnDevice(project, RevokePermissionsCommand()) + } + + fun revokePermissionsAndRestart(project: Project) { + executeOnDevice(project, RevokePermissionsAndRestartCommand()) + } + + fun startDefaultActivity(project: Project) { + executeOnDevice(project, StartDefaultActivityCommand(false)) + } + + fun startDefaultActivityWithDebugger(project: Project) { + executeOnDevice(project, StartDefaultActivityCommand(true)) + } + + fun restartDefaultActivity(project: Project) { + executeOnDevice(project, RestartPackageCommand()) + } + + fun restartDefaultActivityWithDebugger(project: Project) { + executeOnDevice(project, CommandList(KillCommand(), StartDefaultActivityCommand(true))) + } + + fun clearData(project: Project) { + executeOnDevice(project, ClearDataCommand()) + } + + fun getPackageDetail(project: Project, packageName: String, callback: Function1) { + executeOnDevice(project, PackageDetailCommand(packageName, callback)) + } + + fun forceStop(project: Project, packageName: String) { + executeOnDevice(project, ForceStopCommand(packageName)) + } + + fun getPackagePath(project: Project, packageName: String, callback: Function1) { + executeOnDevice(project, PackagePathCommand(packageName, callback)) + } + + fun getActivityService(project: Project, packageName: String, callback: Function1) { + executeOnDevice(project, ActivityServiceCommand(packageName, callback)) + } + + fun clearDataAndRestart(project: Project) { + executeOnDevice(project, ClearDataAndRestartCommand()) + } + + fun getAllApplicationList(project: Project, parameter: String, callback: Function1, Unit>) { + executeOnDevice(project, GetApplicationListCommand(parameter, callback)) + } + + private fun executeOnDevice(project: Project?, runnable: Command) { + + if (AdbUtil.isGradleSyncInProgress(project!!)) { + NotificationHelper.error("Gradle sync is in progress") + return + } + + val result = project!!.getComponent(ObjectGraph::class.java).deviceResultFetcher.fetch() + + if (result != null) { + for (device in result.devices) { + EXECUTOR.submit { runnable.run(project, device, result.facet, result.packageName) } + } + } else { + NotificationHelper.error("No Device found") + } + } + + fun clearData(project: Project, realPackageName: String) { + executeOnDevice(project, ClearDataCommand(realPackageName)) + } + + fun showForegroundActivity(project: Project, callback: Function1) { + executeOnDevice(project, ForegroundActivityCommand(callback)) + } + + fun monkeyTest(project: Project, packageName: String, count: Int, callback: Function1) { + executeOnDevice(project, MonkeyTestCommand(packageName, count, callback)) + } + + fun putStringToDevice(project: Project?, str: String) { + executeOnDevice(project, PutStringToDeviceCommand(str)) + } + + fun interacting(project: Project, type: Int, action: String, category: String, name: String, boundData: MutableList) { + executeOnDevice(project, getInteractingCommand(type, action, category, name, boundData)) + } + + fun getSimpleInfo(project: Project?, command: String, desc: String, callback: Function1) { + executeOnDevice(project, CommonStringResultCommand(command, desc, callback)) + } + + fun captureScreen(project: Project?, localDir: File, fileName: String) { + executeOnDevice(project, CaptureScreenCommand(localDir, fileName)) + } + + /** + * "bad result" + * @param project + * @param localFile + * @param videoName + * @param length + * @param showTouches + */ + @Deprecated("") + fun recordScreen(project: Project?, localFile: File, videoName: String, length: Int, showTouches: Boolean) { + executeOnDevice(project, ScreenRecordCommand(localFile, videoName, length, showTouches)) + } + + fun pullFile(project: Project?, remotePath: String, localFile: File, deleteRemoteFile: Boolean) { + executeOnDevice(project, PullFileCommand(remotePath, localFile, deleteRemoteFile)) + } + + fun getDeviceModel(project: Project?, function: Function1) { + getSimpleInfo(project, "getprop ro.product.model", "get Device model ") { s -> + function.invoke(s.replace("\n", "").replace("\r", "").replace(" ", "")) + null + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/AdbUtil.kt b/src/main/kotlin/com/developerphil/adbidea/adb/AdbUtil.kt new file mode 100644 index 0000000..46a14a7 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/AdbUtil.kt @@ -0,0 +1,73 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.* +import com.android.tools.idea.gradle.project.sync.GradleSyncState +import com.developerphil.adbidea.adb.command.receiver.GenericReceiver +import com.developerphil.adbidea.ui.NotificationHelper.info +import com.intellij.openapi.project.Project +import org.joor.Reflect +import java.io.File +import java.io.IOException +import java.util.concurrent.TimeUnit + + +object AdbUtil { + @Throws(TimeoutException::class, AdbCommandRejectedException::class, ShellCommandUnresponsiveException::class, IOException::class) + fun isAppInstalled(device: IDevice, packageName: String): Boolean { + val receiver = GenericReceiver() + // "pm list packages com.my.package" will return one line per package installed that corresponds to this package. + // if this list is empty, we know for sure that the app is not installed + device.executeShellCommand("pm list packages $packageName", receiver, 15L, TimeUnit.SECONDS) + //TODO make sure that it is the exact package name and not a subset. + // e.g. if our app is called com.example but there is another app called com.example.another.app, it will match and return a false positive + return receiver.adbOutputLines.isNotEmpty() + } + + // The android debugger class is not available in Intellij 2016.1. + // Nobody should use that version but it's still the minimum "supported" version since android studio 2.2 + // shares the same base version. + val isDebuggingAvailable: Boolean + get() = try { + Reflect.on("com.android.tools.idea.run.editor.AndroidDebugger").get() + true + } catch (e: Exception) { + false + } + + fun isGradleSyncInProgress(project: Project): Boolean { + return try { + GradleSyncState.getInstance(project).isSyncInProgress + } catch (t: Throwable) { + info("Couldn't determine if a gradle sync is in progress") + false + } + } + + + @Throws(IOException::class, AdbCommandRejectedException::class, com.android.ddmlib.TimeoutException::class, SyncException::class) + fun pullFile(device: IDevice, remote: String, local: String, monitor: SyncService.ISyncProgressMonitor) { + var sync: SyncService? = null + try { + val targetFileName = File(remote).getName() + Log.d(targetFileName, String.format("Downloading %1\$s from device '%2\$s'", targetFileName, device.serialNumber)) + sync = device.syncService + if (sync == null) { + throw IOException("Unable to open sync connection!") + } + val message = String.format("Downloading file from device '%1\$s'", device.serialNumber) + Log.d("Device", message) + sync.pullFile(remote, local, monitor) + } catch (var11: com.android.ddmlib.TimeoutException) { + Log.e("Device", "Error during Sync: timeout.") + throw var11 + } catch (var12: SyncException) { + Log.e("Device", String.format("Error during Sync: %1\$s", var12.message)) + throw var12 + } catch (var13: IOException) { + Log.e("Device", String.format("Error during Sync: %1\$s", var13.message)) + throw var13 + } finally { + sync?.close() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/Bridge.kt b/src/main/kotlin/com/developerphil/adbidea/adb/Bridge.kt index c9531be..c3cad2a 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/Bridge.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/Bridge.kt @@ -3,7 +3,6 @@ package com.developerphil.adbidea.adb import com.android.ddmlib.IDevice import com.intellij.openapi.project.Project import org.jetbrains.android.sdk.AndroidSdkUtils -import javax.inject.Inject interface Bridge { fun isReady(): Boolean @@ -11,7 +10,7 @@ interface Bridge { } -class BridgeImpl @Inject constructor(project: Project) : Bridge { +class BridgeImpl(project: Project) : Bridge { val androidBridge = AndroidSdkUtils.getDebugBridge(project) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/DeviceResultFetcher.kt b/src/main/kotlin/com/developerphil/adbidea/adb/DeviceResultFetcher.kt index 7b2d1de..5493066 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/DeviceResultFetcher.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/DeviceResultFetcher.kt @@ -5,7 +5,6 @@ import com.android.tools.idea.gradle.project.model.AndroidModuleModel import com.developerphil.adbidea.ui.DeviceChooserDialog import com.developerphil.adbidea.ui.ModuleChooserDialogHelper import com.developerphil.adbidea.ui.NotificationHelper -import com.google.common.collect.Lists import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import org.jetbrains.android.facet.AndroidFacet @@ -15,8 +14,8 @@ import org.jetbrains.android.util.AndroidUtils class DeviceResultFetcher constructor(private val project: Project, private val useSameDevicesHelper: UseSameDevicesHelper, private val bridge: Bridge) { fun fetch(): DeviceResult? { - val facets = getApplicationFacets(project) - if (!facets.isEmpty()) { + val facets = AndroidUtils.getApplicationFacets(project) + if (facets.isNotEmpty()) { val facet = getFacet(facets) ?: return null val packageName = AndroidModuleModel.get(facet)?.applicationId ?: return null @@ -31,12 +30,10 @@ class DeviceResultFetcher constructor(private val project: Project, private val } val devices = bridge.connectedDevices() - if (devices.size == 1) { - return DeviceResult(devices, facet, packageName) - } else if (devices.size > 1) { - return showDeviceChooserDialog(facet, packageName) - } else { - return null + return when { + devices.size == 1 -> DeviceResult(devices, facet, packageName) + devices.size > 1 -> showDeviceChooserDialog(facet, packageName) + else -> null } } return null @@ -71,7 +68,7 @@ class DeviceResultFetcher constructor(private val project: Project, private val useSameDevicesHelper.rememberDevices() } - if (selectedDevices.size == 0) { + if (selectedDevices.isEmpty()) { return null } @@ -79,26 +76,6 @@ class DeviceResultFetcher constructor(private val project: Project, private val } - companion object { - fun getApplicationFacets(project: Project): List { - - val facets = Lists.newArrayList() - for (facet in AndroidUtils.getApplicationFacets(project)) { - if (!isTestProject(facet)) { - facets.add(facet) - } - } - - return facets - } - - fun isTestProject(facet: AndroidFacet): Boolean { - return facet.manifest != null - && facet.manifest!!.instrumentations != null - && !facet.manifest!!.instrumentations.isEmpty() - } - } - } diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt index 9df9219..f1d8895 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt @@ -4,7 +4,7 @@ import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper -import com.developerphil.adbidea.ui.NotificationHelper.* +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet @@ -25,7 +25,7 @@ class ActivityServiceCommand(private val mPackageName: String,private val callba } val receiver = PrintReceiver() device.executeShellCommand("dumpsys activity services $mPackageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s get activity service on %s", mPackageName, device.name)) + NotificationHelper.info(String.format("%s get activity service on %s", mPackageName, device.name)) val string = receiver.toString() callback.invoke(string) val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt index 7cc0a05..a65881c 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt @@ -7,10 +7,8 @@ import com.developerphil.adbidea.ui.NotificationHelper import com.intellij.notification.NotificationType import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project -import javafx.application.Application import org.jetbrains.android.facet.AndroidFacet import java.io.File -import java.util.* import java.util.concurrent.TimeUnit /** @@ -18,7 +16,7 @@ import java.util.concurrent.TimeUnit * Description : */ class CaptureScreenCommand(val localDir: File, val fileName: String) : Command { - override fun run(project: Project, device: IDevice, facet: AndroidFacet?, packageName: String?): Boolean { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { try { val remotePath = "/sdcard/$fileName" val receiver = PrintReceiver() diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ClearDataAndRestartCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ClearDataAndRestartCommand.kt new file mode 100644 index 0000000..20b92b3 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ClearDataAndRestartCommand.kt @@ -0,0 +1,3 @@ +package com.developerphil.adbidea.adb.command + +class ClearDataAndRestartCommand : CommandList(ClearDataCommand(), StartDefaultActivityCommand(false)) \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ClearDataCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ClearDataCommand.kt new file mode 100644 index 0000000..815cf0c --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ClearDataCommand.kt @@ -0,0 +1,32 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil +import com.developerphil.adbidea.adb.command.receiver.GenericReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class ClearDataCommand(val realPackageName:String = "") : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + var packageName = packageName + if (realPackageName.isNotEmpty()) { + packageName = realPackageName + } + try { + if (AdbUtil.isAppInstalled(device, packageName)) { + device.executeShellCommand("pm clear $packageName", GenericReceiver(), 15L, TimeUnit.SECONDS) + NotificationHelper.info(String.format("%s cleared data for app on %s", packageName, device.name)) + return true + } else { + NotificationHelper.error(String.format("%s is not installed on %s", packageName, device.name)) + } + } catch (e1: Exception) { + NotificationHelper.error("Clear data failed... " + e1.message) + } + + return false + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/Command.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/Command.kt new file mode 100644 index 0000000..b18520d --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/Command.kt @@ -0,0 +1,12 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet + +interface Command { + /** + * @return true if the command executed properly + */ + fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/CommandList.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/CommandList.kt new file mode 100644 index 0000000..28db9d0 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CommandList.kt @@ -0,0 +1,20 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet + +open class CommandList(vararg commands: Command) : Command { + + private val commands = listOf(*commands) + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + for (command in commands) { + if (!command.run(project, device, facet, packageName)) { + return false + } + } + return true + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt index f914267..61e54bb 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt @@ -3,7 +3,7 @@ package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper -import com.developerphil.adbidea.ui.NotificationHelper.* +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet @@ -21,7 +21,7 @@ class CommonStringResultCommand(private val commandStr:String,private val operat val receiver = PrintReceiver() device.executeShellCommand(commandStr, receiver, 15L, TimeUnit.SECONDS) if (callback==null) { - info("$operationDesc on ${device.name}\n") + NotificationHelper.info("$operationDesc on ${device.name}\n") val string = receiver.toString() val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) notification.notify(project) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ForceStopCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForceStopCommand.kt index 31305dc..9ff5340 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/ForceStopCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForceStopCommand.kt @@ -4,7 +4,7 @@ import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper -import com.developerphil.adbidea.ui.NotificationHelper.* +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet @@ -22,7 +22,7 @@ class ForceStopCommand(private val mPackageName: String) : Command { if (isAppInstalled(device, mPackageName)) { val receiver = PrintReceiver() device.executeShellCommand("am force-stop $mPackageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("ForceStop %s on %s", mPackageName, device.name)) + NotificationHelper.info(String.format("ForceStop %s on %s", mPackageName, device.name)) val string = receiver.toString() val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) notification.notify(project) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt index 71e20e5..100ba88 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt @@ -3,7 +3,7 @@ package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper -import com.developerphil.adbidea.ui.NotificationHelper.* +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet @@ -20,7 +20,7 @@ class ForegroundActivityCommand(private val callback:(String)->Unit) : Command { try { val receiver = PrintReceiver() device.executeShellCommand("dumpsys activity activities | grep mFocusedActivity", receiver, 15L, TimeUnit.SECONDS) - info(String.format(" get foreground Activity on %s",device.name)) + NotificationHelper.info(String.format(" get foreground Activity on %s",device.name)) val string = receiver.toString() callback.invoke(string) val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/GrantPermissionsCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/GrantPermissionsCommand.kt new file mode 100644 index 0000000..67b00c8 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/GrantPermissionsCommand.kt @@ -0,0 +1,63 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil +import com.developerphil.adbidea.adb.command.receiver.GenericReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.* +import java.util.concurrent.TimeUnit +import java.util.function.Consumer + +class GrantPermissionsCommand : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + try { + if (deviceHasMarshmallow(device)) if (AdbUtil.isAppInstalled(device, packageName)) { + val shellOutputReceiver = GenericReceiver() + device.executeShellCommand("dumpsys package $packageName", shellOutputReceiver, 15L, TimeUnit.SECONDS) + val adbOutputLines = getRequestedPermissions(shellOutputReceiver.adbOutputLines) + NotificationHelper.info(adbOutputLines.toTypedArray().contentToString()) + adbOutputLines.forEach(Consumer { s: String -> + try { + device.executeShellCommand("pm grant $packageName $s", GenericReceiver(), 15L, TimeUnit.SECONDS) + NotificationHelper.info(String.format("Permission %s granted on %s", s, device.name)) + } catch (e: Exception) { + NotificationHelper.error(String.format("Granting %s failed on %s: %s", s, device.name, e.message)) + } + }) + return true + } else { + NotificationHelper.error(String.format("%s is not installed on %s", packageName, device.name)) + } else { + NotificationHelper.error(String.format("%s must be at least api level 23", device.name)) + } + } catch (e1: Exception) { + NotificationHelper.error("Granting permissions fail... " + e1.message) + } + return false + } + + private fun deviceHasMarshmallow(device: IDevice): Boolean { + return device.version.apiLevel >= 23 + } + + private fun getRequestedPermissions(list: List): List { + var requestedPermissionsSection = false + val requestPermissions: MutableList = ArrayList() + for (s in list) { + if (!s.contains(".permission.")) { + requestedPermissionsSection = false + } + if (s.contains("requested permissions:")) { + requestedPermissionsSection = true + continue + } + if (requestedPermissionsSection) { + val permissionName = s.replace(":", "").trim { it <= ' ' } + requestPermissions.add(permissionName) + } + } + return requestPermissions + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/InstallApkCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/InstallApkCommand.kt index 45c605e..bb4a2d8 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/InstallApkCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/InstallApkCommand.kt @@ -14,8 +14,8 @@ import java.util.concurrent.TimeUnit * Description : */ -class InstallApkCommand(val apks: MutableList) : Command { - override fun run(project: Project, device: IDevice, facet: AndroidFacet, pn: String?): Boolean { +class InstallApkCommand(val apks: List) : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { try { device.installPackages(apks, true, listOf(), 15, TimeUnit.SECONDS) info(String.format("Install %d apk file to %s", apks.size, device.name)) @@ -25,4 +25,6 @@ class InstallApkCommand(val apks: MutableList) : Command { } return false } + + } diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/KillCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/KillCommand.kt new file mode 100644 index 0000000..d1d3696 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/KillCommand.kt @@ -0,0 +1,28 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil +import com.developerphil.adbidea.adb.command.receiver.GenericReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.error +import com.developerphil.adbidea.ui.NotificationHelper.info +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class KillCommand : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + try { + if (AdbUtil.isAppInstalled(device, packageName)) { + device.executeShellCommand("am force-stop $packageName", GenericReceiver(), 15L, TimeUnit.SECONDS) + info(String.format("%s forced-stop on %s", packageName, device.name)) + return true + } else { + error(String.format("%s is not installed on %s", packageName, device.name)) + } + } catch (e1: Exception) { + error("Kill fail... " + e1.message) + } + return false + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt index 6a7d6cd..eda9085 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt @@ -3,7 +3,7 @@ package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper -import com.developerphil.adbidea.ui.NotificationHelper.* +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet @@ -22,7 +22,7 @@ class MonkeyTestCommand(private val mPackageName: String, private val count: Int } sb.append("-v $count") device.executeShellCommand(sb.toString(), receiver, 15L, TimeUnit.SECONDS) - info(String.format(" start monkey test on %s", device.name)) + NotificationHelper.info(String.format(" start monkey test on %s", device.name)) val string = receiver.toString() callback.invoke(string) val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt index e64a1d1..9af46a0 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt @@ -4,7 +4,7 @@ import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper -import com.developerphil.adbidea.ui.NotificationHelper.* +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet @@ -23,7 +23,7 @@ class PackageDetailCommand(private val mPackageName: String,private val callback if (isAppInstalled(device, mPackageName)) { val receiver = PrintReceiver() device.executeShellCommand("dumpsys package $mPackageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s get package detail on %s", mPackageName, device.name)) + NotificationHelper.info(String.format("%s get package detail on %s", mPackageName, device.name)) val string = receiver.toString() callback.invoke(string) val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt index 3bd4203..65b21f7 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt @@ -4,7 +4,7 @@ import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper -import com.developerphil.adbidea.ui.NotificationHelper.* +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet @@ -23,7 +23,7 @@ class PackagePathCommand(private val mPackageName: String,private val callback:( if (isAppInstalled(device, mPackageName)) { val receiver = PrintReceiver() device.executeShellCommand("pm path $mPackageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s get package path on %s", mPackageName, device.name)) + NotificationHelper.info(String.format("%s get package path on %s", mPackageName, device.name)) val string = receiver.toString() callback.invoke(string) val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/PutStringToDeviceCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/PutStringToDeviceCommand.kt index 14d0b02..1a79958 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/PutStringToDeviceCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PutStringToDeviceCommand.kt @@ -12,12 +12,12 @@ import java.util.concurrent.TimeUnit * Description : */ class PutStringToDeviceCommand(val str:String):Command{ - override fun run(project: Project?, device: IDevice?, facet: AndroidFacet?, packageName: String?): Boolean { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { try { val receiver = PrintReceiver() device?.executeShellCommand("input text $str", receiver, 15L, TimeUnit.SECONDS) if (!receiver.toString().isNullOrEmpty()) { - NotificationHelper.error("Put String to device :\n " + receiver.toString()) + NotificationHelper.error("Put String to device :\n $receiver") } return true } catch (e1: Exception) { diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/RestartPackageCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/RestartPackageCommand.kt new file mode 100644 index 0000000..eb92ef2 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/RestartPackageCommand.kt @@ -0,0 +1,3 @@ +package com.developerphil.adbidea.adb.command + +class RestartPackageCommand : CommandList(KillCommand(), StartDefaultActivityCommand(false)) \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/RevokePermissionsAndRestartCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/RevokePermissionsAndRestartCommand.kt new file mode 100644 index 0000000..4fd0908 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/RevokePermissionsAndRestartCommand.kt @@ -0,0 +1,3 @@ +package com.developerphil.adbidea.adb.command + +class RevokePermissionsAndRestartCommand : CommandList(RevokePermissionsCommand(), StartDefaultActivityCommand(false)) \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/RevokePermissionsCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/RevokePermissionsCommand.kt new file mode 100644 index 0000000..39106c5 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/RevokePermissionsCommand.kt @@ -0,0 +1,42 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil +import com.developerphil.adbidea.adb.command.receiver.GenericReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.error +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class RevokePermissionsCommand : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + try { + if (deviceHasMarshmallow(device)) if (AdbUtil.isAppInstalled(device, packageName)) { + val shellOutputReceiver = GenericReceiver() + device.executeShellCommand("dumpsys package $packageName", shellOutputReceiver, 15L, TimeUnit.SECONDS) + shellOutputReceiver.adbOutputLines.stream() //only granted permissions, they come in "android.permission.CAMERA: granted=true" + .filter { s: String -> s.contains("permission") }.filter { s: String -> s.contains("granted=true") } //just the permission name is important + .map { s: String -> s.split(":".toRegex()).toTypedArray()[0].trim { it <= ' ' } } + .forEach { s: String -> + try { + device.executeShellCommand("pm revoke $packageName $s", GenericReceiver(), 15L, TimeUnit.SECONDS) + NotificationHelper.info(String.format("Permission %s revoked on %s", s, device.name)) + } catch (e: Exception) { + error(String.format("Revoking %s failed on %s: %s", s, device.name, e.message)) + } + } + return true + } else { + error(String.format("%s is not installed on %s", packageName, device.name)) + } else { + error(String.format("%s must be at least api level 23", device.name)) + } + } catch (e1: Exception) { + error("Revoking permissions fail... " + e1.message) + } + return false + } + + private fun deviceHasMarshmallow(device: IDevice) = device.version.apiLevel >= 23 +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/StartDefaultActivityCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/StartDefaultActivityCommand.kt new file mode 100644 index 0000000..60823e4 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/StartDefaultActivityCommand.kt @@ -0,0 +1,73 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.android.ddmlib.MultiLineReceiver +import com.android.tools.idea.run.activity.ActivityLocator.ActivityLocatorException +import com.android.tools.idea.run.activity.DefaultActivityLocator +import com.developerphil.adbidea.adb.ShellCommandsFactory.startActivity +import com.developerphil.adbidea.debugger.Debugger +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.error +import com.developerphil.adbidea.ui.NotificationHelper.info +import com.google.common.base.Joiner +import com.google.common.base.Strings +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.ThrowableComputable +import org.jetbrains.android.facet.AndroidFacet +import java.util.* +import java.util.concurrent.TimeUnit + +class StartDefaultActivityCommand(private val withDebugger: Boolean) : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + try { + val activityName = getDefaultActivityName(facet, device) + val receiver = StartActivityReceiver() + val shellCommand = startActivity(packageName, activityName, withDebugger) + device.executeShellCommand(shellCommand, receiver, 15L, TimeUnit.SECONDS) + if (withDebugger) { + Debugger(project, device, packageName).attach() + } + if (receiver.isSuccess) { + info(String.format("%s started on %s", packageName, device.name)) + return true + } else { + error(String.format("%s could not be started on %s. \n\nADB Output: \n%s", packageName, device.name, receiver.message)) + } + } catch (e: Exception) { + error("Start fail... " + e.message) + } + return false + } + + @Throws(ActivityLocatorException::class) + private fun getDefaultActivityName(facet: AndroidFacet, device: IDevice): String { + return ApplicationManager.getApplication() + .runReadAction(ThrowableComputable { DefaultActivityLocator(facet).getQualifiedActivityName(device) }) + } + + class StartActivityReceiver : MultiLineReceiver() { + var message = "Nothing Received" + var currentLines: MutableList = ArrayList() + override fun processNewLines(strings: Array) { + for (s in strings) { + if (!Strings.isNullOrEmpty(s)) { + currentLines.add(s) + } + } + computeMessage() + } + + private fun computeMessage() { + message = Joiner.on("\n").join(currentLines) + } + + override fun isCancelled(): Boolean { + return false + } + + val isSuccess: Boolean + get() = currentLines.size in 1..2 + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/UninstallCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/UninstallCommand.kt new file mode 100644 index 0000000..79d6b75 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/UninstallCommand.kt @@ -0,0 +1,30 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.android.ddmlib.InstallException +import com.developerphil.adbidea.ui.NotificationHelper.error +import com.developerphil.adbidea.ui.NotificationHelper.info +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet + +class UninstallCommand(val mPackageName:String = "") : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + var packageName = packageName + if (mPackageName.isNotEmpty()) { + packageName = mPackageName + } + try { + val errorCode = device.uninstallPackage(packageName) + if (errorCode == null) { + info(String.format("%s uninstalled on %s", packageName, device.name)) + return true + } else { + error(String.format("%s is not installed on %s", packageName, device.name)) + } + } catch (e1: InstallException) { + error("Uninstall fail... " + e1.message) + } + + return false + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/receiver/GenericReceiver.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/receiver/GenericReceiver.kt new file mode 100644 index 0000000..c0f0663 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/receiver/GenericReceiver.kt @@ -0,0 +1,35 @@ +package com.developerphil.adbidea.adb.command.receiver + +import com.android.ddmlib.MultiLineReceiver +import java.util.* +import java.util.regex.Pattern + +class GenericReceiver : MultiLineReceiver() { + + val adbOutputLines: MutableList = ArrayList() + private var errorMessage: String? = null + + override fun processNewLines(lines: Array) { + adbOutputLines.addAll(listOf(*lines)) + for (line in lines) { + if (line.isNotEmpty()) { + errorMessage = if (line.startsWith(SUCCESS_OUTPUT)) { + null + } else { + val m = FAILURE_PATTERN.matcher(line) + if (m.matches()) { + m.group(1) + } else { + "Unknown failure" + } + } + } + } + } + + override fun isCancelled() = false + +} + +private const val SUCCESS_OUTPUT = "Success" //$NON-NLS-1$ +private val FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]") //$NON-NLS-1$ diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.kt new file mode 100644 index 0000000..4dcb534 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.kt @@ -0,0 +1,27 @@ +package com.developerphil.adbidea.adb.command.receiver + +import com.android.ddmlib.IShellOutputReceiver +import com.developerphil.adbidea.ui.Utils.Companion.isEmpty +import com.google.common.base.Charsets + +class PrintReceiver : IShellOutputReceiver { + private var mString: String? = null + override fun addOutput(data: ByteArray, offset: Int, length: Int) { + if (!this.isCancelled) { + mString = """ + ${String(data, offset, length, Charsets.UTF_8)} + + """.trimIndent() + } + } + + override fun flush() {} + fun done() {} + override fun isCancelled(): Boolean { + return false + } + + override fun toString(): String { + return (if (isEmpty(mString)) "" else mString)!! + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/compatibility/BackwardCompatibleGetter.kt b/src/main/kotlin/com/developerphil/adbidea/compatibility/BackwardCompatibleGetter.kt new file mode 100644 index 0000000..c83a861 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/compatibility/BackwardCompatibleGetter.kt @@ -0,0 +1,34 @@ +package com.developerphil.adbidea.compatibility + +import org.joor.ReflectException + +/** + * Abstracts the logic to call the current implementation and fall back on reflection for previous versions + */ +abstract class BackwardCompatibleGetter { + fun get(): T { + return try { + getCurrentImplementation() + } catch (error: LinkageError) { + getPreviousImplementation() + } catch (e: Throwable) { + if (isReflectiveException(e)) { + getPreviousImplementation() + } else { + throw RuntimeException(e) + } + } + } + + private fun isReflectiveException(t: Throwable): Boolean { + return t is ClassNotFoundException || + t is NoSuchFieldException || + t is LinkageError || + t is NoSuchMethodException || + t is ReflectException + } + + abstract fun getCurrentImplementation() : T + + abstract fun getPreviousImplementation() : T +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/debugger/Debugger.kt b/src/main/kotlin/com/developerphil/adbidea/debugger/Debugger.kt index 90ee436..dfb64ce 100644 --- a/src/main/kotlin/com/developerphil/adbidea/debugger/Debugger.kt +++ b/src/main/kotlin/com/developerphil/adbidea/debugger/Debugger.kt @@ -38,7 +38,6 @@ class Debugger(private val project: Project, private val device: IDevice, privat if (handler is AndroidProcessHandler) { val client = handler.getClient(selectedClient.device) if (client != null && client.clientData.pid == pid) { - handler.setNoKill() handler.detachProcess() handler.notifyTextAvailable("Disconnecting run session: a new debug session will be established.\n", ProcessOutputTypes.STDOUT) break diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/BoundTableModel.kt b/src/main/kotlin/com/developerphil/adbidea/ui/BoundTableModel.kt new file mode 100644 index 0000000..cc80e87 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/BoundTableModel.kt @@ -0,0 +1,91 @@ +package com.developerphil.adbidea.ui + +import com.developerphil.adbidea.bean.BoundDataType +import com.developerphil.adbidea.bean.BoundItemBean +import java.util.* +import javax.swing.table.AbstractTableModel + +class BoundTableModel : AbstractTableModel() { + private val cellType = arrayOf>(Boolean::class.java, String::class.java, String::class.java, BoundDataType::class.java) + private val title = arrayOf("select", "key", "value", "type") + + private val data = ArrayList() + + + fun getData(): List { + return data.filter { it.selected&& it.key.isNotEmpty()} + } + + fun setData(data: List) { + this.data.clear() + this.data.addAll(data) + fireTableDataChanged() + } + + fun clear() { + val size = this.data.size + this.data.clear() + fireTableRowsDeleted(0, size) + } + + fun addEmptyRow() { + data.add(BoundItemBean(true, "", "", BoundDataType.STRING)) + fireTableRowsInserted(data.size - 1, data.size - 1) + } + + fun removeRow(row: Int) { + if (row > -1 && row < data.size) { + data.removeAt(row) + } + fireTableRowsDeleted(row, row) + } + + override fun getColumnClass(arg0: Int): Class<*> { + return cellType[arg0] + } + + override fun getColumnName(arg0: Int): String { + return title[arg0] + } + + override fun getColumnCount(): Int { + return title.size + } + + override fun getRowCount(): Int { + return data.size + } + + override fun getValueAt(rowIndex: Int, columnIndex: Int): Any? { + if (rowIndex < data.size) { + when (columnIndex) { + 0 -> return data[rowIndex].selected + 1 -> return data[rowIndex].key + 2 -> return data[rowIndex].value + 3 -> return data[rowIndex].dataType + } + } + return null + } + + //重写isCellEditable方法 + + override fun isCellEditable(rowIndex: Int, columnIndex: Int): Boolean { + return true + } + + //重写setValueAt方法 + override fun setValueAt(aValue: Any?, rowIndex: Int, columnIndex: Int) { + if (rowIndex < data.size) { + when (columnIndex) { + 0 -> data[rowIndex].selected = aValue as Boolean + 1 -> data[rowIndex].key = aValue as String + 2 -> data[rowIndex].value = aValue as String + 3 -> data[rowIndex].dataType = aValue as BoundDataType + } + } + this.fireTableCellUpdated(rowIndex, columnIndex) + } + + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/BoundTypeCellEditor.kt b/src/main/kotlin/com/developerphil/adbidea/ui/BoundTypeCellEditor.kt new file mode 100644 index 0000000..bd16496 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/BoundTypeCellEditor.kt @@ -0,0 +1,12 @@ +package com.developerphil.adbidea.ui + +import com.developerphil.adbidea.bean.BoundDataType +import javax.swing.DefaultCellEditor +import javax.swing.JComboBox + +/** + * Created by XQ Yang on 10/10/2018 2:12 PM. + * Description : + */ + +class BoundTypeCellEditor : DefaultCellEditor(JComboBox(BoundDataType.values())) diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/BoundTypeJTable.kt b/src/main/kotlin/com/developerphil/adbidea/ui/BoundTypeJTable.kt new file mode 100644 index 0000000..ba159e4 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/BoundTypeJTable.kt @@ -0,0 +1,21 @@ +package com.developerphil.adbidea.ui + +import javax.swing.JTable +import javax.swing.table.TableCellEditor + +/** + * Created by XQ Yang on 10/10/2018 2:08 PM. + * Description : + */ + +class BoundTypeJTable : JTable() { + + private val mBoundTypeCellEditor = BoundTypeCellEditor() + + override fun getCellEditor(row: Int, column: Int): TableCellEditor { + return if (column == 3) { + mBoundTypeCellEditor + } else super.getCellEditor(row, column) + } +} + diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/DeviceChooserListener.kt b/src/main/kotlin/com/developerphil/adbidea/ui/DeviceChooserListener.kt new file mode 100644 index 0000000..b4299e8 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/DeviceChooserListener.kt @@ -0,0 +1,5 @@ +package com.developerphil.adbidea.ui + +interface DeviceChooserListener { + fun selectedDevicesChanged() +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/ModuleChooserDialogHelper.kt b/src/main/kotlin/com/developerphil/adbidea/ui/ModuleChooserDialogHelper.kt new file mode 100644 index 0000000..ed78568 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/ModuleChooserDialogHelper.kt @@ -0,0 +1,99 @@ +package com.developerphil.adbidea.ui + +import com.google.common.collect.Lists +import com.intellij.ide.util.PropertiesComponent +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet + + +object ModuleChooserDialogHelper { + + val SELECTED_MODULE_PROPERTY = ModuleChooserDialogHelper::class.java.canonicalName + "-SELECTED_MODULE" + val DEFAULT_MODULE_PROPERTY = ModuleChooserDialogHelper::class.java.canonicalName + "-DEFAULT_MODULE" + + val DO_NOT_SELECT_THE_DEFAULT_MODULE = "Do not select the default module" + + fun showDialogForFacets(project: Project, facets: List, isSetDefault: Boolean): AndroidFacet? { + val modules = Lists.newArrayList() + val modulesName = Lists.newArrayList() + val previousModuleName = getPreviousModuleName(project) + val defaultModuleName = getDefaultModuleName(project) + var previousSelectedModule: List? = null + for (facet in facets) { + val module = facet.module + val name = module.name + if (!isSetDefault && name == defaultModuleName) { + return facet + } + modules.add(module) + modulesName.add(name) + if (name == previousModuleName) { + previousSelectedModule = Lists.newArrayList(name) + } else if (isSetDefault && name == defaultModuleName) { + previousSelectedModule = Lists.newArrayList(name) + } + } + + if (isSetDefault && previousSelectedModule == null) { + previousSelectedModule = Lists.newArrayList(DO_NOT_SELECT_THE_DEFAULT_MODULE) + } + + if (isSetDefault) { + modulesName.add(DO_NOT_SELECT_THE_DEFAULT_MODULE) + } + var titleSelectDefault = "Choose Default Module" + if (!Utils.isEmpty(defaultModuleName)) { + titleSelectDefault += ",Current module is :" + defaultModuleName!! + } + val dialog = MyChooseModulesDialog(project, modulesName, if (isSetDefault) titleSelectDefault else "Choose Module", + if (isSetDefault) "Set the default module for each operation" else "", ModuleType.get(modules[0]).icon) + dialog.setSingleSelectionMode() + if (previousSelectedModule != null) { + dialog.selectElements(previousSelectedModule) + } + dialog.show() + + val chosenElements = dialog.chosenElements + if (chosenElements.isEmpty()) { + return null + } + + val chosenModule = chosenElements[0] + if (isSetDefault) { + if (chosenModule == DO_NOT_SELECT_THE_DEFAULT_MODULE) { + saveDefaultModuleName(project, "") + return null + } else { + saveDefaultModuleName(project, chosenModule) + } + } else { + saveModuleName(project, chosenModule) + } + val chosenModuleIndex = modulesName.indexOf(chosenModule) + return facets[chosenModuleIndex] + } + + private fun saveDefaultModuleName(project: Project, moduleName: String) { + val properties = PropertiesComponent.getInstance(project) + properties.setValue(DEFAULT_MODULE_PROPERTY, moduleName) + } + + private fun saveModuleName(project: Project, moduleName: String) { + val properties = PropertiesComponent.getInstance(project) + properties.setValue(SELECTED_MODULE_PROPERTY, moduleName) + } + + private fun getPreviousModuleName(project: Project): String? { + val properties = PropertiesComponent.getInstance(project) + return properties.getValue(SELECTED_MODULE_PROPERTY) + } + + private fun getDefaultModuleName(project: Project): String? { + val properties = PropertiesComponent.getInstance(project) + return properties.getValue(DEFAULT_MODULE_PROPERTY) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/MyApplistModel.kt b/src/main/kotlin/com/developerphil/adbidea/ui/MyApplistModel.kt new file mode 100644 index 0000000..893606e --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/MyApplistModel.kt @@ -0,0 +1,27 @@ +package com.developerphil.adbidea.ui + +import javax.swing.AbstractListModel + +/** + * Created by XQ Yang on 10/8/2018 5:41 PM. + * Description : + */ + +internal class MyApplistModel(private val mList: MutableList) : AbstractListModel() { + + override fun getSize(): Int { + return mList.size + } + + override fun getElementAt(index: Int): String { + return mList[index] + } + + fun delete(s: String) { + val index = mList.indexOf(s) + if (index != -1) { + mList.removeAt(index) + fireIntervalRemoved(this, index, index) + } + } +} diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/MyChooseModulesDialog.kt b/src/main/kotlin/com/developerphil/adbidea/ui/MyChooseModulesDialog.kt new file mode 100644 index 0000000..7b42f9c --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/MyChooseModulesDialog.kt @@ -0,0 +1,22 @@ +package com.developerphil.adbidea.ui + +import com.intellij.icons.AllIcons +import com.intellij.ide.util.ChooseElementsDialog +import com.intellij.openapi.project.Project +import javax.swing.Icon + +class MyChooseModulesDialog(project: Project, items: List, title: String, description: String, private val mIcon: Icon) : ChooseElementsDialog(project, items, + title, description, false) { + + fun setSingleSelectionMode() { + this.myChooser.setSingleSelectionMode() + } + + override fun getItemText(s: String): String { + return s + } + + override fun getItemIcon(s: String): Icon? { + return if (s == ModuleChooserDialogHelper.DO_NOT_SELECT_THE_DEFAULT_MODULE) AllIcons.Actions.Clear else mIcon + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/MyDeviceChooser.kt b/src/main/kotlin/com/developerphil/adbidea/ui/MyDeviceChooser.kt new file mode 100755 index 0000000..facbc6e --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/MyDeviceChooser.kt @@ -0,0 +1,387 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.developerphil.adbidea.ui + +import com.android.ddmlib.AndroidDebugBridge +import com.android.ddmlib.IDevice +import com.android.ddmlib.IDevice.HardwareFeature +import com.android.tools.idea.run.ConnectedAndroidDevice +import com.android.tools.idea.run.LaunchCompatibility +import com.android.tools.idea.run.LaunchCompatibilityCheckerImpl +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.util.Condition +import com.intellij.openapi.util.text.StringUtil +import com.intellij.ui.ColoredTableCellRenderer +import com.intellij.ui.DoubleClickListener +import com.intellij.ui.ScrollPaneFactory +import com.intellij.ui.SimpleTextAttributes +import com.intellij.ui.table.JBTable +import com.intellij.util.Alarm +import com.intellij.util.ThreeState +import com.intellij.util.containers.ContainerUtil +import gnu.trove.TIntArrayList +import org.jetbrains.android.dom.manifest.UsesFeature +import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.android.sdk.AndroidSdkUtils +import java.awt.Dimension +import java.awt.event.KeyAdapter +import java.awt.event.KeyEvent +import java.awt.event.MouseEvent +import java.util.* +import java.util.concurrent.atomic.AtomicReference +import javax.swing.Action +import javax.swing.JComponent +import javax.swing.JTable +import javax.swing.ListSelectionModel +import javax.swing.table.AbstractTableModel + +/** + * @author Eugene.Kudelevsky + */ +class MyDeviceChooser(multipleSelection: Boolean, + okAction: Action, + private val myFacet: AndroidFacet, + private val myFilter: Condition?) : Disposable { + private val myListeners = ContainerUtil.createLockFreeCopyOnWriteList() + private val myRefreshingAlarm: Alarm + private val myBridge: AndroidDebugBridge? + @Volatile + private var myProcessSelectionFlag = true + /** The current list of devices that is displayed in the table. */ + private var myDisplayedDevices = EMPTY_DEVICE_ARRAY + /** + * The current list of devices obtained from the debug bridge. This is updated in a background thread. + * If it is different than [.myDisplayedDevices], then a [.refreshTable] invocation in the EDT thread + * will update the displayed list to match the detected list. + */ + private val myDetectedDevicesRef = AtomicReference(EMPTY_DEVICE_ARRAY) + private val myPanel: JComponent + private val myDeviceTable: JBTable + private var mySelectedRows: IntArray? = null + private var hadUserInteraction = false + private var previouslySelectedSerials: Array? = null + private fun setColumnWidth(deviceTable: JBTable, columnIndex: Int, sampleText: String) { + val width = getWidth(deviceTable, sampleText) + deviceTable.columnModel.getColumn(columnIndex).preferredWidth = width + } + + private fun getWidth(deviceTable: JBTable, sampleText: String): Int { + val metrics = deviceTable.getFontMetrics(deviceTable.font) + return metrics.stringWidth(sampleText) + } + + fun init(selectedSerials: Array?) { + previouslySelectedSerials = selectedSerials + updateTable() + addUpdatingRequest() + } + + fun init(selectedSerials: List) { + init(selectedSerials.toTypedArray()) + } + + private fun updatePreviouslySelectedSerials() { + if (previouslySelectedSerials != null && !hadUserInteraction) { + resetSelection(previouslySelectedSerials!!) + } + } + + private val myUpdateRequest = Runnable { + updateTable() + addUpdatingRequest() + } + + private fun addUpdatingRequest() { + if (myRefreshingAlarm.isDisposed) { + return + } + myRefreshingAlarm.cancelAllRequests() + myRefreshingAlarm.addRequest(myUpdateRequest, REFRESH_INTERVAL_MS) + } + + private fun resetSelection(selectedSerials: Array) { + val model = myDeviceTable.model as MyDeviceTableModel + val selectedSerialsSet = mutableSetOf() + Collections.addAll(selectedSerialsSet, *selectedSerials) + val myDevices = model.myDevices + val selectionModel = myDeviceTable.selectionModel + var cleared = false + var i = 0 + val n = myDevices.size + while (i < n) { + val serialNumber = myDevices[i].serialNumber + if (selectedSerialsSet.contains(serialNumber)) { + if (!cleared) { + selectionModel.clearSelection() + cleared = true + } + selectionModel.addSelectionInterval(i, i) + } + i++ + } + } + + fun updateTable() { + val devices = myBridge?.let { getFilteredDevices(it) } ?: EMPTY_DEVICE_ARRAY + if (devices.size > 1) { // sort by API level + Arrays.sort(devices, object : Comparator { + override fun compare(device1: IDevice, device2: IDevice): Int { + val apiLevel1 = safeGetApiLevel(device1) + val apiLevel2 = safeGetApiLevel(device2) + return apiLevel2 - apiLevel1 + } + + private fun safeGetApiLevel(device: IDevice): Int { + return try { + val s = device.getProperty(IDevice.PROP_BUILD_API_LEVEL) + if (StringUtil.isNotEmpty(s)) s.toInt() else 0 + } catch (e: Exception) { + 0 + } + } + }) + } + if (!Arrays.equals(myDisplayedDevices, devices)) { + myDetectedDevicesRef.set(devices) + ApplicationManager.getApplication().invokeLater({ refreshTable() }, ModalityState.stateForComponent(myDeviceTable)) + } + } + + private fun refreshTable() { + val devices = myDetectedDevicesRef.get() + myDisplayedDevices = devices + val selectedDevices = selectedDevices + val selectedRows = TIntArrayList() + for (i in devices.indices) { + if (selectedDevices.contains(devices[i])) { + selectedRows.add(i) + } + } + myProcessSelectionFlag = false + myDeviceTable.setModel(MyDeviceTableModel(devices)) + if (selectedRows.size() == 0 && devices.size > 0) { + myDeviceTable.selectionModel.setSelectionInterval(0, 0) + } + for (selectedRow in selectedRows.toNativeArray()) { + if (selectedRow < devices.size) { + myDeviceTable.selectionModel.addSelectionInterval(selectedRow, selectedRow) + } + } + fireSelectedDevicesChanged() + myProcessSelectionFlag = true + updatePreviouslySelectedSerials() + } + + fun hasDevices(): Boolean { + return myDetectedDevicesRef.get().isNotEmpty() + } + + val preferredFocusComponent: JComponent + get() = myDeviceTable + + val panel: JComponent? + get() = myPanel + + val selectedDevices: Array + get() { + val rows = if (mySelectedRows != null) mySelectedRows!! else myDeviceTable.selectedRows + val result = mutableListOf() + for (row in rows) { + if (row >= 0) { + val serial = myDeviceTable.getValueAt(row, SERIAL_COLUMN_INDEX) + val bridge = AndroidSdkUtils.getDebugBridge(myFacet.module.project) ?: return EMPTY_DEVICE_ARRAY + val devices = getFilteredDevices(bridge) + for (device in devices) { + if (device.serialNumber == serial.toString()) { + result.add(device) + break + } + } + } + } + return result.toTypedArray() + } + + private fun getFilteredDevices(bridge: AndroidDebugBridge): Array { + val filteredDevices: MutableList = ArrayList() + for (device in bridge.devices) { + if (myFilter == null || myFilter.value(device)) { + filteredDevices.add(device) + } + } + // Do not filter launching cloud devices as they are just unselectable progress markers +// that are replaced with the actual cloud devices as soon as they are up and the actual cloud devices will be filtered above. + return filteredDevices.toTypedArray() + } + + fun finish() { + mySelectedRows = myDeviceTable.selectedRows + } + + override fun dispose() {} + fun setEnabled(enabled: Boolean) { + myDeviceTable.isEnabled = enabled + } + + fun fireSelectedDevicesChanged() { + for (listener in myListeners) { + listener.selectedDevicesChanged() + } + } + + fun addListener(listener: DeviceChooserListener) { + myListeners.add(listener) + } + + private inner class MyDeviceTableModel(val myDevices: Array) : AbstractTableModel() { + override fun getColumnName(column: Int): String { + return COLUMN_TITLES[column] + } + + override fun getRowCount(): Int { + return myDevices.size + } + + override fun getColumnCount(): Int { + return COLUMN_TITLES.size + } + + override fun getValueAt(rowIndex: Int, columnIndex: Int): Any? { + if (rowIndex >= myDevices.size) { + return null + } + val device = myDevices[rowIndex] + when (columnIndex) { + DEVICE_NAME_COLUMN_INDEX -> return generateDeviceName(device) + SERIAL_COLUMN_INDEX -> return device.serialNumber + DEVICE_STATE_COLUMN_INDEX -> return getDeviceState(device) + COMPATIBILITY_COLUMN_INDEX -> return LaunchCompatibilityCheckerImpl.create(myFacet, null, null).validate(ConnectedAndroidDevice(device, null)) + } + return null + } + + private fun generateDeviceName(device: IDevice): String { + return device.name + .replace(device.serialNumber, "") + .replace("[-_]".toRegex(), " ") + .replace("[\\[\\]]".toRegex(), "") + } + + override fun getColumnClass(columnIndex: Int): Class<*> { + return if (columnIndex == COMPATIBILITY_COLUMN_INDEX) { + LaunchCompatibility::class.java + } else if (columnIndex == DEVICE_NAME_COLUMN_INDEX) { + IDevice::class.java + } else { + String::class.java + } + } + + } + + private class LaunchCompatibilityRenderer : ColoredTableCellRenderer() { + override fun customizeCellRenderer(table: JTable, value: Any?, selected: Boolean, hasFocus: Boolean, row: Int, column: Int) { + if (value !is LaunchCompatibility) { + return + } + val compatibility = value + val compatible = compatibility.isCompatible + if (compatible == ThreeState.YES) { + append("Yes") + } else { + if (compatible == ThreeState.NO) { + append("No", SimpleTextAttributes.ERROR_ATTRIBUTES) + } else { + append("Maybe") + } + val reason = compatibility.reason + if (reason != null) { + append(", ") + append(reason) + } + } + } + } + + companion object { + private val COLUMN_TITLES = arrayOf("Device", "Serial Number", "State", "Compatible") + private const val DEVICE_NAME_COLUMN_INDEX = 0 + private const val SERIAL_COLUMN_INDEX = 1 + private const val DEVICE_STATE_COLUMN_INDEX = 2 + private const val COMPATIBILITY_COLUMN_INDEX = 3 + private const val REFRESH_INTERVAL_MS = 500 + val EMPTY_DEVICE_ARRAY = arrayOf() + private fun getRequiredHardwareFeatures(requiredFeatures: List): EnumSet { // Currently, this method is hardcoded to only search if the list of required features includes a watch. +// We may not want to search the device for every possible feature, but only a small subset of important +// features, starting with hardware type watch.. + for (feature in requiredFeatures) { + val name = feature.name + if (name != null && UsesFeature.HARDWARE_TYPE_WATCH == name.stringValue) { + return EnumSet.of(HardwareFeature.WATCH) + } + } + return EnumSet.noneOf(HardwareFeature::class.java) + } + + private fun getDeviceState(device: IDevice): String { + val state = device.state + return if (state != null) StringUtil.capitalize(state.name.toLowerCase()) else "" + } + } + + init { + myDeviceTable = JBTable() + myPanel = ScrollPaneFactory.createScrollPane(myDeviceTable) + myPanel.preferredSize = Dimension(450, 220) + myDeviceTable.model = MyDeviceTableModel(EMPTY_DEVICE_ARRAY) + myDeviceTable.setSelectionMode(if (multipleSelection) ListSelectionModel.MULTIPLE_INTERVAL_SELECTION else ListSelectionModel.SINGLE_SELECTION) + myDeviceTable.selectionModel.addListSelectionListener { + if (myProcessSelectionFlag) { + hadUserInteraction = true + fireSelectedDevicesChanged() + } + } + object : DoubleClickListener() { + override fun onDoubleClick(e: MouseEvent): Boolean { + if (myDeviceTable.isEnabled && okAction.isEnabled) { + okAction.actionPerformed(null) + return true + } + return false + } + }.installOn(myDeviceTable) + myDeviceTable.setDefaultRenderer(LaunchCompatibility::class.java, LaunchCompatibilityRenderer()) + myDeviceTable.addKeyListener(object : KeyAdapter() { + override fun keyPressed(e: KeyEvent) { + if (e.keyCode == KeyEvent.VK_ENTER && okAction.isEnabled) { + okAction.actionPerformed(null) + } + } + }) + setColumnWidth(myDeviceTable, DEVICE_NAME_COLUMN_INDEX, "Samsung Galaxy Nexus Android 4.1 (API 17)") + setColumnWidth(myDeviceTable, SERIAL_COLUMN_INDEX, "0000-0000-00000") + setColumnWidth(myDeviceTable, DEVICE_STATE_COLUMN_INDEX, "offline") + setColumnWidth(myDeviceTable, COMPATIBILITY_COLUMN_INDEX, "yes") + // Do not recreate columns on every model update - this should help maintain the column sizes set above + myDeviceTable.autoCreateColumnsFromModel = false + // Allow sorting by columns (in lexicographic order) + myDeviceTable.autoCreateRowSorter = true + myRefreshingAlarm = Alarm(Alarm.ThreadToUse.POOLED_THREAD, this) + myBridge = AndroidSdkUtils.getDebugBridge(myFacet.module.project) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/NotificationHelper.kt b/src/main/kotlin/com/developerphil/adbidea/ui/NotificationHelper.kt new file mode 100644 index 0000000..4ec7278 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/NotificationHelper.kt @@ -0,0 +1,23 @@ +package com.developerphil.adbidea.ui + +import com.intellij.notification.Notification +import com.intellij.notification.NotificationGroup +import com.intellij.notification.NotificationListener +import com.intellij.notification.NotificationType +import javax.swing.event.HyperlinkEvent + +object NotificationHelper { + val INFO = NotificationGroup.logOnlyGroup("ADB Idea (Logging)") + val ERRORS = NotificationGroup.balloonGroup("ADB Idea (Errors)") + val NOOP_LISTENER = NotificationListener { _: Notification?, _: HyperlinkEvent? -> } + + fun info(message: String) = sendNotification(message, NotificationType.INFORMATION, INFO) + + fun error(message: String) = sendNotification(message, NotificationType.ERROR, ERRORS) + + private fun sendNotification(message: String, notificationType: NotificationType, notificationGroup: NotificationGroup) { + notificationGroup.createNotification("ADB IDEA", escapeString(message), notificationType, NOOP_LISTENER).notify(null) + } + + private fun escapeString(string: String) = string.replace("\n".toRegex(), "\n
") +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/ui/Utils.kt b/src/main/kotlin/com/developerphil/adbidea/ui/Utils.kt new file mode 100644 index 0000000..88c3dbe --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/ui/Utils.kt @@ -0,0 +1,53 @@ +package com.developerphil.adbidea.ui + +import java.awt.Color +import javax.swing.JTextPane +import javax.swing.text.BadLocationException +import javax.swing.text.MutableAttributeSet +import javax.swing.text.SimpleAttributeSet +import javax.swing.text.StyleConstants + +/** + * Created by XQ Yang on 2018/6/25 18:14. + * Description : + */ + +class Utils { + companion object { + + fun isEmpty(str: CharSequence?): Boolean { + return str == null || str.isEmpty() + } + + + @Synchronized + fun append2TextPane(str: String, color: Color?, textPane: JTextPane) { + val doc = textPane.document + if (doc != null) { + try { + var attr: MutableAttributeSet? = null + if (color != null) { + attr = SimpleAttributeSet() + StyleConstants.setForeground(attr, color) + StyleConstants.setBold(attr, true) + } + doc.insertString(doc.length, str, attr) + } catch (e: BadLocationException) { + } + + } + } + + fun append2TextPane(str: String, textPane: JTextPane) { + append2TextPane(str, null, textPane) + } + + fun append2TextPaneNewLine(str: String, color: Color, textPane: JTextPane) { + append2TextPane(str + "\n", color, textPane) + } + + fun append2TextPaneNewLine(str: String, textPane: JTextPane) { + append2TextPane(str + "\n", null, textPane) + } + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 7a207b8..0ff58e7 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,7 +1,7 @@ com.developerphil.adbidea ADB Idea - 1.6.4 + 1.6.5 Philippe Breault and Void Young 1.6.5 +
    +
  • update to origin : support Android Studio 3.6+
  • +
1.6.4
  • BUGFIX: The result of multi-module selection is inconsistent with the actual selection
  • -
1.6.3 + + 1.6.3
  • update to origin : BUGFIX: Show all connected devices on Android Studio 3.4+
@@ -147,11 +152,11 @@
  • Now support projects with more than one application module
  • ]]> - + - - - + + + com.intellij.modules.platform org.jetbrains.android diff --git a/src/test/java/com/developerphil/adbidea/adb/command/StartDefaultActivityCommandTest.java b/src/test/java/com/developerphil/adbidea/adb/command/StartDefaultActivityCommandTest.java deleted file mode 100644 index b4956c5..0000000 --- a/src/test/java/com/developerphil/adbidea/adb/command/StartDefaultActivityCommandTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.developerphil.adbidea.adb.command; - -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; - -public class StartDefaultActivityCommandTest { - - private static final String[] TRAILING_EMPTY_LINE = new String[]{""}; - - @Test - public void testReceiverSuccess() throws Exception { - String[] lines = new String[]{ - "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.untitled/.MyActivity }" - }; - - StartDefaultActivityCommand.StartActivityReceiver receiver = new StartDefaultActivityCommand.StartActivityReceiver(); - assertThat(receiver.isSuccess(), is(false)); - - receiver.processNewLines(lines); - receiver.processNewLines(TRAILING_EMPTY_LINE); - assertThat(receiver.isSuccess(), is(true)); - } - - @Test - public void isSuccessWhenAppIsAlreadyStarted() throws Exception { - String[] lines = new String[]{ - "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.untitled/.MyActivity }", - "Warning: Activity not started, its current task has been brought to the front" - }; - - StartDefaultActivityCommand.StartActivityReceiver receiver = new StartDefaultActivityCommand.StartActivityReceiver(); - receiver.processNewLines(lines); - receiver.processNewLines(TRAILING_EMPTY_LINE); - - assertThat(receiver.isSuccess(), is(true)); - } - - - @Test - public void isFailureWhenAppIsUninstalled() throws Exception { - String[] lines = new String[]{ - "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.cxategory.LAUNCHER] cmp=com.example.untitled/.MyActivity }", - "Error type 3", - "Error: Activity class {com.example.untitled/com.example.untitled.MyActivity} does not exist." - }; - - StartDefaultActivityCommand.StartActivityReceiver receiver = new StartDefaultActivityCommand.StartActivityReceiver(); - receiver.processNewLines(lines); - receiver.processNewLines(TRAILING_EMPTY_LINE); - - assertThat(receiver.isSuccess(), is(false)); - - assertThat(receiver.getMessage(), is(equalTo( - "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.cxategory.LAUNCHER] cmp=com.example.untitled/.MyActivity }\n" + - "Error type 3\n" + - "Error: Activity class {com.example.untitled/com.example.untitled.MyActivity} does not exist." - ))); - - } -} diff --git a/src/test/java/com/developerphil/adbidea/adb/command/receiver/GenericReceiverTest.java b/src/test/java/com/developerphil/adbidea/adb/command/receiver/GenericReceiverTest.java deleted file mode 100644 index 6a2e5ee..0000000 --- a/src/test/java/com/developerphil/adbidea/adb/command/receiver/GenericReceiverTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.developerphil.adbidea.adb.command.receiver; - -import org.junit.Test; - -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.matchers.JUnitMatchers.hasItems; - -public class GenericReceiverTest { - - @Test - public void testReceiverRecordsAdbOutput() throws Exception { - GenericReceiver receiver = new GenericReceiver(); - assertTrue(receiver.getAdbOutputLines().isEmpty()); - - receiver.processNewLines(new String[]{"1", "2", "3"}); - assertThat(receiver.getAdbOutputLines(), hasItems("1", "2", "3")); - - receiver.processNewLines(new String[]{"4"}); - assertThat(receiver.getAdbOutputLines(), hasItems("1", "2", "3", "4")); - } - -} diff --git a/src/test/java/com/developerphil/adbidea/compatibility/BackwardCompatibleGetterTest.java b/src/test/java/com/developerphil/adbidea/compatibility/BackwardCompatibleGetterTest.java deleted file mode 100644 index 911f56e..0000000 --- a/src/test/java/com/developerphil/adbidea/compatibility/BackwardCompatibleGetterTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.developerphil.adbidea.compatibility; - -import com.developerphil.adbidea.test.Holder; -import org.joor.ReflectException; -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -public class BackwardCompatibleGetterTest { - - - @Test - public void onlyCallCurrentImplementationWhenItIsValid() throws Exception { - final Holder currentImplementation = new Holder(false); - - new BackwardCompatibleGetter() { - @Override - protected Boolean getCurrentImplementation() throws Throwable { - currentImplementation.set(true); - return true; - } - - @Override - protected Boolean getPreviousImplementation() { - fail("should not be called"); - return true; - } - }.get(); - - assertThat(currentImplementation.get(), is(true)); - - } - - @Test - public void callPreviousImplementationWhenCurrentThrowsErrors() throws Exception { - expectPreviousImplementationIsCalledFor(new ClassNotFoundException()); - expectPreviousImplementationIsCalledFor(new NoSuchMethodException()); - expectPreviousImplementationIsCalledFor(new NoSuchFieldException()); - expectPreviousImplementationIsCalledFor(new LinkageError()); - expectPreviousImplementationIsCalledFor(new ReflectException()); - } - - - @Test(expected = RuntimeException.class) - public void throwExceptionsWhenTheyAreNotRelatedToBackwardCompatibility() throws Exception { - new BackwardCompatibleGetter() { - @Override - protected Boolean getCurrentImplementation() throws Throwable { - throw new RuntimeException("exception!"); - } - - @Override - protected Boolean getPreviousImplementation() { - fail("should not be called"); - return true; - } - }.get(); - } - - - private static void expectPreviousImplementationIsCalledFor(final Throwable throwable) throws Exception { - final Holder calledPreviousImplementation = new Holder(); - new BackwardCompatibleGetter() { - @Override - protected Boolean getCurrentImplementation() throws Throwable { - throw throwable; - } - - @Override - protected Boolean getPreviousImplementation() { - calledPreviousImplementation.set(true); - return null; - } - }.get(); - - assertThat(calledPreviousImplementation.get(), is(true)); - } - -} \ No newline at end of file diff --git a/src/test/java/com/developerphil/adbidea/test/Holder.java b/src/test/java/com/developerphil/adbidea/test/Holder.java deleted file mode 100644 index f7e7027..0000000 --- a/src/test/java/com/developerphil/adbidea/test/Holder.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.developerphil.adbidea.test; - -public class Holder { - - private T value; - - public Holder() { - - } - - public Holder(T defaultValue) { - value = defaultValue; - } - - public void set(T value) { - this.value = value; - } - - public T get() { - return this.value; - } -} diff --git a/src/test/kotlin/com/developerphil/adbidea/adb/FakeDevice.kt b/src/test/kotlin/com/developerphil/adbidea/adb/FakeDevice.kt index 5be9d80..eda723e 100644 --- a/src/test/kotlin/com/developerphil/adbidea/adb/FakeDevice.kt +++ b/src/test/kotlin/com/developerphil/adbidea/adb/FakeDevice.kt @@ -17,227 +17,243 @@ data class FakeDevice(private val serialNumber: String) : IDevice { // NOT IMPLEMENTED // --------------- + override fun installRemotePackage(p0: String?, p1: Boolean, p2: InstallReceiver?, vararg p3: String?) { + throw UnsupportedOperationException("not implemented") + } + + override fun installRemotePackage(p0: String?, p1: Boolean, p2: InstallReceiver?, p3: Long, p4: Long, p5: TimeUnit?, vararg p6: String?) { + throw UnsupportedOperationException("not implemented") + } + + override fun installPackage(p0: String?, p1: Boolean, p2: InstallReceiver?, vararg p3: String?) { + throw UnsupportedOperationException("not implemented") + } + + override fun installPackage(p0: String?, p1: Boolean, p2: InstallReceiver?, p3: Long, p4: Long, p5: TimeUnit?, vararg p6: String?) { + throw UnsupportedOperationException("not implemented") + } + override fun executeShellCommand(p0: String?, p1: IShellOutputReceiver?, p2: Long, p3: Long, p4: TimeUnit?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun isOffline(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun startScreenRecorder(p0: String?, p1: ScreenRecorderOptions?, p2: IShellOutputReceiver?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun reboot(p0: String?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getMountPoint(p0: String?): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getClients(): Array { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun runLogService(p0: String?, p1: LogReceiver?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun installRemotePackage(p0: String?, p1: Boolean, vararg p2: String?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getVersion(): AndroidVersion { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getClientName(p0: Int): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun isOnline(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun runEventLogService(p0: LogReceiver?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getLanguage(): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun root(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun isBootLoader(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getSystemProperty(p0: String?): Future { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun isEmulator(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getFileListingService(): FileListingService { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun isRoot(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun removeForward(p0: Int, p1: Int) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun removeForward(p0: Int, p1: String?, p2: IDevice.DeviceUnixSocketNamespace?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun createForward(p0: Int, p1: Int) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun createForward(p0: Int, p1: String?, p2: IDevice.DeviceUnixSocketNamespace?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getAbis(): MutableList { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun pushFile(p0: String?, p1: String?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun removeRemotePackage(p0: String?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getName(): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getClient(p0: String?): Client { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getBattery(): Future { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getBattery(p0: Long, p1: TimeUnit?): Future { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun executeShellCommand(p0: String?, p1: IShellOutputReceiver?, p2: Int) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun executeShellCommand(p0: String?, p1: IShellOutputReceiver?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun executeShellCommand(p0: String?, p1: IShellOutputReceiver?, p2: Long, p3: TimeUnit?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun hasClients(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getPropertySync(p0: String?): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getProperties(): MutableMap { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getProperty(p0: String?): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getAvdName(): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getRegion(): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getState(): IDevice.DeviceState { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getPropertyCacheOrSync(p0: String?): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun installPackages(p0: MutableList?, p1: Boolean, p2: MutableList?, p3: Long, p4: TimeUnit?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun pullFile(p0: String?, p1: String?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getDensity(): Int { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun uninstallPackage(p0: String?): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getSyncService(): SyncService { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun installPackage(p0: String?, p1: Boolean, vararg p2: String?) { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun syncPackageToDevice(p0: String?): String { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun arePropertiesSet(): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun supportsFeature(p0: IDevice.Feature?): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun supportsFeature(p0: IDevice.HardwareFeature?): Boolean { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getScreenshot(): RawImage { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getScreenshot(p0: Long, p1: TimeUnit?): RawImage { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getBatteryLevel(): Int { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getBatteryLevel(p0: Long): Int { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } override fun getPropertyCount(): Int { - throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + throw UnsupportedOperationException("not implemented") } } \ No newline at end of file diff --git a/src/test/kotlin/com/developerphil/adbidea/adb/command/StartDefaultActivityCommandTest.kt b/src/test/kotlin/com/developerphil/adbidea/adb/command/StartDefaultActivityCommandTest.kt new file mode 100644 index 0000000..3e0bfeb --- /dev/null +++ b/src/test/kotlin/com/developerphil/adbidea/adb/command/StartDefaultActivityCommandTest.kt @@ -0,0 +1,52 @@ +package com.developerphil.adbidea.adb.command + +import com.developerphil.adbidea.adb.command.StartDefaultActivityCommand.StartActivityReceiver +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class StartDefaultActivityCommandTest { + + @Test + fun testReceiverSuccess() { + with(StartActivityReceiver()) { + assertThat(isSuccess).isFalse() + processNewLines(arrayOf( + "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.untitled/.MyActivity }" + )) + processNewLines(TRAILING_EMPTY_LINE) + assertThat(isSuccess).isTrue() + } + } + + @Test + fun testIsSuccessWhenAppIsAlreadyStarted() { + with(StartActivityReceiver()) { + processNewLines(arrayOf( + "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.untitled/.MyActivity }", + "Warning: Activity not started, its current task has been brought to the front" + )) + processNewLines(TRAILING_EMPTY_LINE) + assertThat(isSuccess).isTrue() + } + } + + @Test + fun testIsFailureWhenAppIsUninstalled() { + with(StartActivityReceiver()) { + processNewLines(arrayOf( + "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.cxategory.LAUNCHER] cmp=com.example.untitled/.MyActivity }", + "Error type 3", + "Error: Activity class {com.example.untitled/com.example.untitled.MyActivity} does not exist." + )) + processNewLines(TRAILING_EMPTY_LINE) + assertThat(isSuccess).isFalse() + assertThat(message).isEqualTo( + "Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.cxategory.LAUNCHER] cmp=com.example.untitled/.MyActivity }\n" + + "Error type 3\n" + + "Error: Activity class {com.example.untitled/com.example.untitled.MyActivity} does not exist." + ) + } + } +} + +private val TRAILING_EMPTY_LINE = arrayOf("") diff --git a/src/test/kotlin/com/developerphil/adbidea/adb/command/receiver/GenericReceiverTest.kt b/src/test/kotlin/com/developerphil/adbidea/adb/command/receiver/GenericReceiverTest.kt new file mode 100644 index 0000000..800d598 --- /dev/null +++ b/src/test/kotlin/com/developerphil/adbidea/adb/command/receiver/GenericReceiverTest.kt @@ -0,0 +1,18 @@ +package com.developerphil.adbidea.adb.command.receiver + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class GenericReceiverTest { + @Test + fun testReceiverRecordsAdbOutput() { + val receiver = GenericReceiver() + assertThat(receiver.adbOutputLines).isEmpty() + + receiver.processNewLines(arrayOf("1", "2", "3")) + assertThat(receiver.adbOutputLines).containsExactly("1", "2", "3") + + receiver.processNewLines(arrayOf("4")) + assertThat(receiver.adbOutputLines).containsExactly("1", "2", "3", "4") + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/developerphil/adbidea/compatibility/BackwardCompatibleGetterTest.kt b/src/test/kotlin/com/developerphil/adbidea/compatibility/BackwardCompatibleGetterTest.kt new file mode 100644 index 0000000..00be35b --- /dev/null +++ b/src/test/kotlin/com/developerphil/adbidea/compatibility/BackwardCompatibleGetterTest.kt @@ -0,0 +1,64 @@ +package com.developerphil.adbidea.compatibility + +import com.google.common.truth.Truth.assertThat +import org.joor.ReflectException +import org.junit.Assert.fail +import org.junit.Test + +class BackwardCompatibleGetterTest { + @Test + fun onlyCallCurrentImplementationWhenItIsValid() { + var value = false + object : BackwardCompatibleGetter() { + override fun getCurrentImplementation(): Boolean { + value = true + return true + } + + override fun getPreviousImplementation(): Boolean { + fail("should not be called") + return true + } + }.get() + assertThat(value).isTrue() + } + + @Test + fun callPreviousImplementationWhenCurrentThrowsErrors() { + expectPreviousImplementationIsCalledFor(ClassNotFoundException()) + expectPreviousImplementationIsCalledFor(NoSuchMethodException()) + expectPreviousImplementationIsCalledFor(NoSuchFieldException()) + expectPreviousImplementationIsCalledFor(LinkageError()) + expectPreviousImplementationIsCalledFor(ReflectException()) + } + + @Test(expected = RuntimeException::class) + fun throwExceptionsWhenTheyAreNotRelatedToBackwardCompatibility() { + object : BackwardCompatibleGetter() { + override fun getCurrentImplementation(): Boolean { + throw RuntimeException("exception!") + } + + override fun getPreviousImplementation(): Boolean { + fail("should not be called") + return false + } + }.get() + } + + private fun expectPreviousImplementationIsCalledFor(throwable: Throwable) { + var value = false + object : BackwardCompatibleGetter() { + override fun getCurrentImplementation(): Boolean { + throw throwable + } + + override fun getPreviousImplementation(): Boolean { + value = true + return true + } + }.get() + + assertThat(value).isTrue() + } +} \ No newline at end of file