From 757a9b85cbc49c8eba1558f56afcf67c569999aa Mon Sep 17 00:00:00 2001 From: longforus Date: Tue, 28 Aug 2018 18:25:38 +0800 Subject: [PATCH 01/43] tentative add application management dialog --- gradle.properties.change_me | 15 - .../adbidea/action/AdbAction.java | 1 + .../adbidea/action/QuickListAction.java | 3 + .../developerphil/adbidea/adb/AdbFacade.java | 8 + .../ui/ApplicationManagementDialog.form | 282 ++++++++++++++++++ .../ui/ApplicationManagementDialog.java | 280 +++++++++++++++++ .../adbidea/ui/DeviceChooserDialog.java | 49 ++- .../com/developerphil/adbidea/ui/Utils.java | 19 ++ .../ApplicationManagementPopupAction.kt | 22 ++ .../adbidea/adb/GetApplicationListCommand.kt | 30 ++ src/main/resources/META-INF/plugin.xml | 9 + 11 files changed, 698 insertions(+), 20 deletions(-) delete mode 100644 gradle.properties.change_me create mode 100644 src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form create mode 100644 src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java create mode 100644 src/main/java/com/developerphil/adbidea/ui/Utils.java create mode 100644 src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/GetApplicationListCommand.kt diff --git a/gradle.properties.change_me b/gradle.properties.change_me deleted file mode 100644 index 91c5847a..00000000 --- a/gradle.properties.change_me +++ /dev/null @@ -1,15 +0,0 @@ -# Copy this file as gradle.properties -# It will be used for both compiling and running the adb-idea plugin. - - -# Path to a downloaded instance of Android Studio -# This is used to add the android plugin dependencies to the project. -# must point to the latest version of Android Studio. -# You'll know it's right if you can find "$StudioCompilePath/lib/idea.jar" -StudioCompilePath=/Applications/Android Studio.app/Contents - - -# Determines which IDE to run when using the "./gradlew runIdea" command. -# This is useful to test the plugin on older versions of Android Studio or Intellij -# Default value: $StudioCompilePath -#StudioRunPath=/Applications/Android Studio.app/Contents \ No newline at end of file diff --git a/src/main/java/com/developerphil/adbidea/action/AdbAction.java b/src/main/java/com/developerphil/adbidea/action/AdbAction.java index 9ba22cbe..c19f1d0d 100644 --- a/src/main/java/com/developerphil/adbidea/action/AdbAction.java +++ b/src/main/java/com/developerphil/adbidea/action/AdbAction.java @@ -7,6 +7,7 @@ public abstract class AdbAction extends AnAction { + @Override public final void actionPerformed(AnActionEvent e) { final Project project = e.getData(PlatformDataKeys.PROJECT); diff --git a/src/main/java/com/developerphil/adbidea/action/QuickListAction.java b/src/main/java/com/developerphil/adbidea/action/QuickListAction.java index ecdd6ac8..5a117b30 100644 --- a/src/main/java/com/developerphil/adbidea/action/QuickListAction.java +++ b/src/main/java/com/developerphil/adbidea/action/QuickListAction.java @@ -10,6 +10,7 @@ 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) { @@ -32,6 +33,8 @@ protected void fillActions(@Nullable final Project project, addAction("com.developerphil.adbidea.action.StartWithDebuggerAction", group); addAction("com.developerphil.adbidea.action.RestartWithDebuggerAction", group); } + group.addSeparator(); + addAction("com.developerphil.adbidea.action.extend.ApplicationManagementPopupAction", group); } diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index 9ab35b04..4b126903 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -7,8 +7,11 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.intellij.openapi.project.Project; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; import static com.developerphil.adbidea.adb.AdbUtil.isGradleSyncInProgress; import static com.developerphil.adbidea.ui.NotificationHelper.error; @@ -61,6 +64,11 @@ 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)) { diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form new file mode 100644 index 00000000..a8291d65 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form @@ -0,0 +1,282 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java new file mode 100644 index 00000000..0e3667a4 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java @@ -0,0 +1,280 @@ +package com.developerphil.adbidea.ui; + +import com.developerphil.adbidea.adb.AdbFacade; +import com.intellij.openapi.project.Project; +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import org.jetbrains.annotations.Nullable; + +/** + * Created by XQ Yang on 8/28/2018 4:13 PM. + * Description : + */ + +public class ApplicationManagementDialog extends JDialog { + private JRadioButton mAllStatusRadioButton; + private JRadioButton mDisabledRadioButton; + private JRadioButton mEnabledRadioButton; + private JRadioButton mAllTypeRadioButton; + private JRadioButton mSystemRadioButton; + private JRadioButton mThirdPartyRadioButton; + private JCheckBox mShowApkFileCheckBox; + private JCheckBox mShowInstallersCheckBox; + private JCheckBox mContainsUninstalledCheckBox; + private JButton mQueryButton; + private JTextField tv_keyword; + private JList mList1; + private JButton mUninstallButton; + private JButton mClearAppCacheDataButton; + private JButton mRunningServicesButton; + private JButton mViewDetailButton; + private JButton mViewPathButton; + private JPanel mPanel; + private JScrollPane sp; + + private static final String PARAMETER_DISABLED = "-d "; + private static final String PARAMETER_ENABLED = "-e "; + private static final String PARAMETER_SYSTEM = "-s "; + private static final String PARAMETER_THIRD_PARTY = "-3 "; + private static final String PARAMETER_INSTALLER = "-i "; + private static final String PARAMETER_UNINSTALLED = "-u "; + private static final String PARAMETER_RELEVANCE_FILE = "-f "; + + private String keyWord = ""; + private String parameter = ""; + private Project mProject; + + public ApplicationManagementDialog(@Nullable Project project) { + setContentPane($$$getRootComponent$$$()); + setModal(true); + Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); + int x = (int) screensize.getWidth() / 2 - getWidth() / 2; + int y = (int) screensize.getHeight() / 2 - getHeight() / 2; + setTitle("Adb Application Management"); + setLocation(x, y); + mProject = project; + + mQueryButton.addActionListener(e -> { + StringBuilder sb = new StringBuilder(); + if (mDisabledRadioButton.isSelected()) { + sb.append(PARAMETER_DISABLED); + } else if (mEnabledRadioButton.isSelected()) { + sb.append(PARAMETER_ENABLED); + } + if (mSystemRadioButton.isSelected()) { + sb.append(PARAMETER_SYSTEM); + } else if (mThirdPartyRadioButton.isSelected()) { + sb.append(PARAMETER_THIRD_PARTY); + } + if (mShowApkFileCheckBox.isSelected()) { + sb.append(PARAMETER_RELEVANCE_FILE); + } + if (mShowInstallersCheckBox.isSelected()) { + sb.append(PARAMETER_INSTALLER); + } + if (mContainsUninstalledCheckBox.isSelected()) { + sb.append(PARAMETER_UNINSTALLED); + } + String keywordText = tv_keyword.getText(); + if (!Utils.isEmpty(keywordText)) { + sb.append(keywordText); + } + AdbFacade.getAllApplicationList(mProject, sb.toString(), strings -> { + mList1.setListData(strings.toArray()); + return null; + }); + }); + } + + { + // GUI initializer generated by IntelliJ IDEA GUI Designer + // >>> IMPORTANT!! <<< + // DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + mPanel = new JPanel(); + mPanel.setLayout(new FormLayout("fill:338px:noGrow,left:4dlu:noGrow,fill:d:noGrow,left:4dlu:noGrow,fill:d:noGrow", + "center:d:noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:d:grow,top:4dlu:noGrow,center:max(d;4px):noGrow")); + mPanel.setName(""); + mPanel.setBorder(BorderFactory.createTitledBorder("All Application")); + final JPanel panel1 = new JPanel(); + panel1.setLayout(new GridBagLayout()); + CellConstraints cc = new CellConstraints(); + mPanel.add(panel1, cc.xy(1, 1)); + panel1.setBorder(BorderFactory.createTitledBorder("Other")); + mShowApkFileCheckBox = new JCheckBox(); + mShowApkFileCheckBox.setText("Show Apk File"); + GridBagConstraints gbc; + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel1.add(mShowApkFileCheckBox, gbc); + mShowInstallersCheckBox = new JCheckBox(); + mShowInstallersCheckBox.setText("Show installers"); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel1.add(mShowInstallersCheckBox, gbc); + mContainsUninstalledCheckBox = new JCheckBox(); + mContainsUninstalledCheckBox.setText("Contains uninstalled"); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel1.add(mContainsUninstalledCheckBox, gbc); + final JPanel panel2 = new JPanel(); + panel2.setLayout(new GridBagLayout()); + mPanel.add(panel2, cc.xy(3, 1)); + panel2.setBorder(BorderFactory.createTitledBorder("Type")); + mAllTypeRadioButton = new JRadioButton(); + mAllTypeRadioButton.setSelected(true); + mAllTypeRadioButton.setText("All"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel2.add(mAllTypeRadioButton, gbc); + mSystemRadioButton = new JRadioButton(); + mSystemRadioButton.setText("System"); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel2.add(mSystemRadioButton, gbc); + mThirdPartyRadioButton = new JRadioButton(); + mThirdPartyRadioButton.setText("Third-party"); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel2.add(mThirdPartyRadioButton, gbc); + final JPanel panel3 = new JPanel(); + panel3.setLayout(new GridBagLayout()); + panel3.setToolTipText("status"); + mPanel.add(panel3, cc.xy(5, 1)); + panel3.setBorder(BorderFactory.createTitledBorder("Status")); + mAllStatusRadioButton = new JRadioButton(); + mAllStatusRadioButton.setSelected(true); + mAllStatusRadioButton.setText("All"); + gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel3.add(mAllStatusRadioButton, gbc); + mEnabledRadioButton = new JRadioButton(); + mEnabledRadioButton.setText("enabled"); + gbc = new GridBagConstraints(); + gbc.gridx = 2; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel3.add(mEnabledRadioButton, gbc); + mDisabledRadioButton = new JRadioButton(); + mDisabledRadioButton.setText("disabled"); + gbc = new GridBagConstraints(); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.WEST; + panel3.add(mDisabledRadioButton, gbc); + mQueryButton = new JButton(); + mQueryButton.setText("Query"); + mPanel.add(mQueryButton, cc.xy(5, 3)); + final JPanel panel4 = new JPanel(); + panel4.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); + mPanel.add(panel4, cc.xyw(1, 3, 3)); + final JLabel label1 = new JLabel(); + label1.setText("Name Filter :"); + panel4.add(label1, + new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + tv_keyword = new JTextField(); + panel4.add(tv_keyword, + new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, + null, new Dimension(150, -1), null, 0, false)); + final JPanel panel5 = new JPanel(); + panel5.setLayout( + new FormLayout("fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:max(d;4px):noGrow", + "center:d:noGrow")); + mPanel.add(panel5, cc.xyw(1, 7, 5)); + mUninstallButton = new JButton(); + mUninstallButton.setText("Uninstall"); + panel5.add(mUninstallButton, cc.xy(1, 1)); + mClearAppCacheDataButton = new JButton(); + mClearAppCacheDataButton.setText("Clear app cache data"); + panel5.add(mClearAppCacheDataButton, cc.xy(3, 1)); + mRunningServicesButton = new JButton(); + mRunningServicesButton.setText("Running Services"); + panel5.add(mRunningServicesButton, cc.xy(5, 1)); + mViewDetailButton = new JButton(); + mViewDetailButton.setText("View Detail"); + panel5.add(mViewDetailButton, cc.xy(7, 1)); + mViewPathButton = new JButton(); + mViewPathButton.setText("View Path"); + panel5.add(mViewPathButton, cc.xy(9, 1)); + sp = new JScrollPane(); + mPanel.add(sp, cc.xyw(1, 5, 5, CellConstraints.FILL, CellConstraints.FILL)); + mList1 = new JList(); + sp.setViewportView(mList1); + ButtonGroup buttonGroup; + buttonGroup = new ButtonGroup(); + buttonGroup.add(mDisabledRadioButton); + buttonGroup.add(mAllStatusRadioButton); + buttonGroup.add(mEnabledRadioButton); + buttonGroup = new ButtonGroup(); + buttonGroup.add(mAllTypeRadioButton); + buttonGroup.add(mSystemRadioButton); + buttonGroup.add(mThirdPartyRadioButton); + } + + /** @noinspection ALL */ + public JComponent $$$getRootComponent$$$() { + return mPanel; + } +} diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java b/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java index 1673d32d..21d74a12 100644 --- a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java @@ -6,6 +6,11 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Insets; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.NotNull; @@ -39,7 +44,7 @@ public DeviceChooserDialog(@NotNull final AndroidFacet facet) { myDeviceChooser = new MyDeviceChooser(true, getOKAction(), facet, facet.getConfiguration().getAndroidTarget(), null); Disposer.register(myDisposable, myDeviceChooser); - myDeviceChooser.addListener(this::updateOkButton); + myDeviceChooser.addListener(this :: updateOkButton); myDeviceChooserWrapper.add(myDeviceChooser.getPanel()); @@ -51,10 +56,7 @@ public DeviceChooserDialog(@NotNull final AndroidFacet facet) { } private void persistSelectedSerialsToPreferences() { - pluginPreferences.saveSelectedDeviceSerials( - stream(myDeviceChooser.getSelectedDevices()) - .map(IDevice::getSerialNumber) - .collect(Collectors.toList())); + pluginPreferences.saveSelectedDeviceSerials(stream(myDeviceChooser.getSelectedDevices()).map(IDevice :: getSerialNumber).collect(Collectors.toList())); } private void updateOkButton() { @@ -113,4 +115,41 @@ public static String toString(@NotNull IDevice[] devices) { return builder.toString(); } + { + // GUI initializer generated by IntelliJ IDEA GUI Designer + // >>> IMPORTANT!! <<< + // DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + myPanel = new JPanel(); + myPanel.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); + myDeviceChooserWrapper = new JPanel(); + myDeviceChooserWrapper.setLayout(new BorderLayout(0, 0)); + myPanel.add(myDeviceChooserWrapper, + new GridConstraints(0, 0, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, + GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + useSameDeviceSCheckBox = new JCheckBox(); + useSameDeviceSCheckBox.setBorderPainted(true); + useSameDeviceSCheckBox.setMargin(new Insets(1, 1, 1, 1)); + useSameDeviceSCheckBox.setMaximumSize(new Dimension(280, 25)); + useSameDeviceSCheckBox.setMinimumSize(new Dimension(280, 25)); + useSameDeviceSCheckBox.setPreferredSize(new Dimension(280, 25)); + useSameDeviceSCheckBox.setText("Use same device(s) for future commands"); + useSameDeviceSCheckBox.setVerticalAlignment(3); + myDeviceChooserWrapper.add(useSameDeviceSCheckBox, BorderLayout.SOUTH); + } + + /** @noinspection ALL */ + public JComponent $$$getRootComponent$$$() { + return myPanel; + } } \ No newline at end of file diff --git a/src/main/java/com/developerphil/adbidea/ui/Utils.java b/src/main/java/com/developerphil/adbidea/ui/Utils.java new file mode 100644 index 00000000..cc70ec91 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/Utils.java @@ -0,0 +1,19 @@ +package com.developerphil.adbidea.ui; + +/** + * Created by XQ Yang on 2018/6/25 18:14. + * Description : + */ + +public class Utils { + public Utils() { + } + + public static boolean isEmpty(CharSequence s) { + if (s == null) { + return true; + } else { + return s.length() == 0; + } + } +} diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt new file mode 100644 index 00000000..470e23f9 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt @@ -0,0 +1,22 @@ +package com.developerphil.adbidea.action.extend + +import com.developerphil.adbidea.action.AdbAction +import com.developerphil.adbidea.ui.ApplicationManagementDialog +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + +/** + * Created by XQ Yang on 8/28/2018 2:53 PM. + * Description : + */ +class ApplicationManagementPopupAction:AdbAction(){ + + + override fun actionPerformed(e: AnActionEvent?, project: Project?) { + val popup = ApplicationManagementDialog(project) + popup.pack() + popup.isVisible = true + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/GetApplicationListCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/GetApplicationListCommand.kt new file mode 100644 index 00000000..defe35a9 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/GetApplicationListCommand.kt @@ -0,0 +1,30 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.GenericReceiver +import com.developerphil.adbidea.ui.NotificationHelper.error +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +/** + * Created by XQ Yang on 8/28/2018 3:33 PM. + * Description : + */ + +class GetApplicationListCommand(private val mParameter: String,private val callback:(List)->Unit) : Command { + + private val genericReceiver = GenericReceiver() + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + try { + device.executeShellCommand("pm list packages $mParameter", genericReceiver, 10L, TimeUnit.SECONDS) + callback.invoke(genericReceiver.adbOutputLines) + return true + } catch (e1: Exception) { + error(String.format("get Application list failure on %s",device.name)) + } + return false + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index bd5b1fcf..f5a8d4cb 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -196,6 +196,15 @@ + + + + + + + From af16832b91d32c1e10c1c1b833a777c9900a7840 Mon Sep 17 00:00:00 2001 From: longforus Date: Wed, 29 Aug 2018 20:48:14 +0800 Subject: [PATCH 02/43] add some command implementation --- .../developerphil/adbidea/adb/AdbFacade.java | 30 +- .../adbidea/adb/command/ClearDataCommand.java | 12 + .../adbidea/adb/command/UninstallCommand.java | 13 + .../adb/command/receiver/PrintReceiver.java | 31 ++ .../ui/ApplicationManagementDialog.form | 58 ++-- .../ui/ApplicationManagementDialog.java | 272 +++++++++++++----- .../adbidea/ui/DeviceChooserDialog.java | 9 +- .../adbidea/ui/NotificationHelper.java | 10 +- .../adbidea/adb/ActivityServiceCommand.kt | 39 +++ .../adbidea/adb/CommonStringResultCommand.kt | 39 +++ .../adbidea/adb/PackageDetailCommand.kt | 39 +++ .../adbidea/adb/PackagePathCommand.kt | 39 +++ 12 files changed, 500 insertions(+), 91 deletions(-) create mode 100644 src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/ActivityServiceCommand.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/PackageDetailCommand.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/PackagePathCommand.kt diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index 4b126903..79e18c7f 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -2,11 +2,20 @@ import com.android.ddmlib.IDevice; import com.developerphil.adbidea.ObjectGraph; -import com.developerphil.adbidea.adb.command.*; +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.GrantPermissionsCommand; +import com.developerphil.adbidea.adb.command.KillCommand; +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.StartDefaultActivityCommand; +import com.developerphil.adbidea.adb.command.UninstallCommand; import com.developerphil.adbidea.ui.NotificationHelper; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.intellij.openapi.project.Project; - import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -20,6 +29,10 @@ 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()); } @@ -59,6 +72,15 @@ public static void restartDefaultActivityWithDebugger(Project project) { 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 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()); @@ -88,4 +110,8 @@ private static void executeOnDevice(final Project project, final Command runnabl error("No Device found"); } } + + public static void clearData(Project project, String realPackageName) { + executeOnDevice(project, new ClearDataCommand(realPackageName)); + } } diff --git a/src/main/java/com/developerphil/adbidea/adb/command/ClearDataCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/ClearDataCommand.java index da5b91c5..269c71bf 100644 --- a/src/main/java/com/developerphil/adbidea/adb/command/ClearDataCommand.java +++ b/src/main/java/com/developerphil/adbidea/adb/command/ClearDataCommand.java @@ -13,8 +13,20 @@ 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); diff --git a/src/main/java/com/developerphil/adbidea/adb/command/UninstallCommand.java b/src/main/java/com/developerphil/adbidea/adb/command/UninstallCommand.java index a3e6b9b6..43986abe 100644 --- a/src/main/java/com/developerphil/adbidea/adb/command/UninstallCommand.java +++ b/src/main/java/com/developerphil/adbidea/adb/command/UninstallCommand.java @@ -9,8 +9,21 @@ 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) { 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 new file mode 100644 index 00000000..561a88ed --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java @@ -0,0 +1,31 @@ +package com.developerphil.adbidea.adb.command.receiver; + +import java.util.List; + +public class PrintReceiver extends GenericReceiver { + + @Override + public String toString() { + List outputLines = getAdbOutputLines(); + if (!outputLines.isEmpty()) { + StringBuilder stringBuilder = new StringBuilder(); + int tabCount = 0; + for (String line : outputLines) { + stringBuilder.append(line); + stringBuilder.append("\n"); + if (line.isEmpty()) { + tabCount = 0; + continue; + } + if (line.endsWith(":")) { + tabCount++; + } + for (int i = 0; i < tabCount; i++) { + stringBuilder.append(" "); + } + } + return stringBuilder.toString(); + } + return super.toString(); + } +} diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form index a8291d65..a547474f 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form @@ -2,30 +2,29 @@
- + + - - + - - - - + - + + + - + @@ -64,7 +63,7 @@ - + @@ -74,7 +73,7 @@ - + @@ -110,7 +109,7 @@ - + @@ -165,15 +164,19 @@ - + - + + + - + - + + + @@ -219,7 +222,7 @@ - + @@ -265,6 +268,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java index 0e3667a4..4957b5e4 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java @@ -2,15 +2,20 @@ import com.developerphil.adbidea.adb.AdbFacade; import com.intellij.openapi.project.Project; +import com.intellij.ui.JBColor; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.core.GridLayoutManager; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; +import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Toolkit; +import java.awt.event.MouseEvent; +import java.util.Collections; +import java.util.List; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JButton; @@ -19,10 +24,19 @@ import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextField; +import javax.swing.JTextPane; +import javax.swing.event.MouseInputAdapter; +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; import org.jetbrains.annotations.Nullable; /** @@ -31,6 +45,7 @@ */ public class ApplicationManagementDialog extends JDialog { + private List mList; private JRadioButton mAllStatusRadioButton; private JRadioButton mDisabledRadioButton; private JRadioButton mEnabledRadioButton; @@ -42,7 +57,7 @@ public class ApplicationManagementDialog extends JDialog { private JCheckBox mContainsUninstalledCheckBox; private JButton mQueryButton; private JTextField tv_keyword; - private JList mList1; + private JList mJList; private JButton mUninstallButton; private JButton mClearAppCacheDataButton; private JButton mRunningServicesButton; @@ -50,6 +65,8 @@ public class ApplicationManagementDialog extends JDialog { private JButton mViewPathButton; private JPanel mPanel; private JScrollPane sp; + private JTextPane tp; + private JScrollPane sp_tp; private static final String PARAMETER_DISABLED = "-d "; private static final String PARAMETER_ENABLED = "-e "; @@ -59,20 +76,37 @@ public class ApplicationManagementDialog extends JDialog { private static final String PARAMETER_UNINSTALLED = "-u "; private static final String PARAMETER_RELEVANCE_FILE = "-f "; - private String keyWord = ""; - private String parameter = ""; private Project mProject; + private final JPopupMenu mPopupMenu; public ApplicationManagementDialog(@Nullable Project project) { - setContentPane($$$getRootComponent$$$()); + setResizable(true); setModal(true); Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); - int x = (int) screensize.getWidth() / 2 - getWidth() / 2; - int y = (int) screensize.getHeight() / 2 - getHeight() / 2; + int x = (int) screensize.getWidth() / 3 - getWidth() / 2; + int y = (int) screensize.getHeight() / 3 - getHeight() / 2; setTitle("Adb Application Management"); setLocation(x, y); mProject = project; - + mPopupMenu = new JPopupMenu(); + JMenuItem clear = mPopupMenu.add(new JMenuItem("clear")); + clear.addActionListener(e -> tp.setText("")); + tp.addMouseListener(new MouseInputAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + mPopupMenu.show(tp, e.getX(), e.getY()); + } + } + }); + mJList.addMouseListener(new MouseInputAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + mJList.clearSelection(); + } + } + }); mQueryButton.addActionListener(e -> { StringBuilder sb = new StringBuilder(); if (mDisabledRadioButton.isSelected()) { @@ -99,10 +133,112 @@ public ApplicationManagementDialog(@Nullable Project project) { sb.append(keywordText); } AdbFacade.getAllApplicationList(mProject, sb.toString(), strings -> { - mList1.setListData(strings.toArray()); + Collections.sort(strings); + mList = strings; + mJList.setListData(strings.toArray()); return null; }); }); + mUninstallButton.addActionListener(e -> { + int[] selectedIndices = mJList.getSelectedIndices(); + for (int index : selectedIndices) { + AdbFacade.uninstall(mProject, getRealPackageName(mList.get(index))); + mJList.remove(index); + } + }); + mClearAppCacheDataButton.addActionListener(e -> { + List selectedValuesList = mJList.getSelectedValuesList(); + for (String packageName : selectedValuesList) { + AdbFacade.clearData(mProject, getRealPackageName(packageName)); + } + }); + mViewDetailButton.addActionListener(e -> { + List selectedValuesList = mJList.getSelectedValuesList(); + for (String packageName : selectedValuesList) { + String name = getRealPackageName(packageName); + append2Ta("View " + name + " detail : \n", JBColor.BLUE); + AdbFacade.getPackageDetail(mProject, name, s -> { + append2Ta(s); + return null; + }); + } + }); + mViewPathButton.addActionListener(e -> { + List selectedValuesList = mJList.getSelectedValuesList(); + for (String packageName : selectedValuesList) { + String name = getRealPackageName(packageName); + append2Ta("View " + name + "apk path : \n", JBColor.BLUE); + AdbFacade.getPackagePath(mProject, name, s -> { + append2Ta(s); + return null; + }); + } + }); + mRunningServicesButton.addActionListener(e -> { + List selectedValuesList = mJList.getSelectedValuesList(); + if (selectedValuesList.isEmpty()) { + String keywordText = tv_keyword.getText(); + if (!Utils.isEmpty(keywordText)) { + append2Ta("View running services related with" + keywordText + " : \n", JBColor.BLUE); + AdbFacade.getActivityService(mProject, keywordText, s -> { + append2Ta(s); + return null; + }); + } else { + append2Ta("View all running services : \n", JBColor.BLUE); + AdbFacade.getActivityService(mProject, "", s -> { + append2Ta(s); + return null; + }); + } + } + for (String packageName : selectedValuesList) { + String name = getRealPackageName(packageName); + append2Ta("View running services related with" + name + " : \n", JBColor.BLUE); + AdbFacade.getActivityService(mProject, name, s -> { + append2Ta(s); + return null; + }); + } + }); + setContentPane($$$getRootComponent$$$()); + } + + private void append2Ta(String str, Color color) { + Document doc = tp.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) { + } + } + } + + private void append2Ta(String str) { + append2Ta(str, null); + } + + private String getRealPackageName(String packageName) { + if (mShowApkFileCheckBox.isSelected() && mShowInstallersCheckBox.isSelected()) { + String[] split = packageName.split("="); + return split[1].split(" ")[0]; + } + if (mShowApkFileCheckBox.isSelected()) { + String[] split = packageName.split("="); + return split[1]; + } + if (mShowInstallersCheckBox.isSelected()) { + String[] split = packageName.split(":"); + return split[1].split(" ")[0]; + } + String[] strings = packageName.split(":"); + return strings[1]; } { @@ -121,14 +257,16 @@ public ApplicationManagementDialog(@Nullable Project project) { */ private void $$$setupUI$$$() { mPanel = new JPanel(); - mPanel.setLayout(new FormLayout("fill:338px:noGrow,left:4dlu:noGrow,fill:d:noGrow,left:4dlu:noGrow,fill:d:noGrow", - "center:d:noGrow,top:4dlu:noGrow,center:max(d;4px):noGrow,top:4dlu:noGrow,center:d:grow,top:4dlu:noGrow,center:max(d;4px):noGrow")); + mPanel.setLayout(new FormLayout("fill:341px:noGrow,fill:d:grow,fill:d:noGrow,fill:d:noGrow", + "center:max(d;4px):noGrow,center:44px:noGrow,center:32px:noGrow,center:d:noGrow,center:32px:noGrow,top:4dlu:noGrow,center:d:grow")); + mPanel.setMinimumSize(new Dimension(590, 280)); mPanel.setName(""); + mPanel.setPreferredSize(new Dimension(590, 280)); mPanel.setBorder(BorderFactory.createTitledBorder("All Application")); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridBagLayout()); CellConstraints cc = new CellConstraints(); - mPanel.add(panel1, cc.xy(1, 1)); + mPanel.add(panel1, cc.xyw(1, 2, 4)); panel1.setBorder(BorderFactory.createTitledBorder("Other")); mShowApkFileCheckBox = new JCheckBox(); mShowApkFileCheckBox.setText("Show Apk File"); @@ -158,10 +296,51 @@ public ApplicationManagementDialog(@Nullable Project project) { gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; panel1.add(mContainsUninstalledCheckBox, gbc); + mQueryButton = new JButton(); + mQueryButton.setText("Query"); + mPanel.add(mQueryButton, cc.xy(4, 3)); final JPanel panel2 = new JPanel(); - panel2.setLayout(new GridBagLayout()); - mPanel.add(panel2, cc.xy(3, 1)); - panel2.setBorder(BorderFactory.createTitledBorder("Type")); + panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); + mPanel.add(panel2, cc.xyw(1, 3, 3)); + final JLabel label1 = new JLabel(); + label1.setText("Name Filter :"); + panel2.add(label1, + new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, + null, 0, false)); + tv_keyword = new JTextField(); + panel2.add(tv_keyword, + new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, + null, new Dimension(150, -1), null, 0, false)); + final JPanel panel3 = new JPanel(); + panel3.setLayout( + new FormLayout("fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:max(d;4px):noGrow", + "center:d:noGrow")); + mPanel.add(panel3, cc.xyw(1, 5, 4)); + mUninstallButton = new JButton(); + mUninstallButton.setText("Uninstall"); + panel3.add(mUninstallButton, cc.xy(1, 1)); + mClearAppCacheDataButton = new JButton(); + mClearAppCacheDataButton.setText("Clear app cache data"); + panel3.add(mClearAppCacheDataButton, cc.xy(3, 1)); + mRunningServicesButton = new JButton(); + mRunningServicesButton.setText("Running Services"); + panel3.add(mRunningServicesButton, cc.xy(5, 1)); + mViewDetailButton = new JButton(); + mViewDetailButton.setText("View Detail"); + panel3.add(mViewDetailButton, cc.xy(7, 1)); + mViewPathButton = new JButton(); + mViewPathButton.setText("View Path"); + panel3.add(mViewPathButton, cc.xy(9, 1)); + sp = new JScrollPane(); + sp.setMinimumSize(new Dimension(0, 100)); + mPanel.add(sp, cc.xyw(1, 4, 4, CellConstraints.FILL, CellConstraints.FILL)); + mJList = new JList(); + mJList.setMinimumSize(new Dimension(0, 100)); + sp.setViewportView(mJList); + final JPanel panel4 = new JPanel(); + panel4.setLayout(new GridBagLayout()); + mPanel.add(panel4, cc.xy(1, 1)); + panel4.setBorder(BorderFactory.createTitledBorder("Type")); mAllTypeRadioButton = new JRadioButton(); mAllTypeRadioButton.setSelected(true); mAllTypeRadioButton.setText("All"); @@ -171,7 +350,7 @@ public ApplicationManagementDialog(@Nullable Project project) { gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; - panel2.add(mAllTypeRadioButton, gbc); + panel4.add(mAllTypeRadioButton, gbc); mSystemRadioButton = new JRadioButton(); mSystemRadioButton.setText("System"); gbc = new GridBagConstraints(); @@ -180,7 +359,7 @@ public ApplicationManagementDialog(@Nullable Project project) { gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; - panel2.add(mSystemRadioButton, gbc); + panel4.add(mSystemRadioButton, gbc); mThirdPartyRadioButton = new JRadioButton(); mThirdPartyRadioButton.setText("Third-party"); gbc = new GridBagConstraints(); @@ -189,12 +368,12 @@ public ApplicationManagementDialog(@Nullable Project project) { gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; - panel2.add(mThirdPartyRadioButton, gbc); - final JPanel panel3 = new JPanel(); - panel3.setLayout(new GridBagLayout()); - panel3.setToolTipText("status"); - mPanel.add(panel3, cc.xy(5, 1)); - panel3.setBorder(BorderFactory.createTitledBorder("Status")); + panel4.add(mThirdPartyRadioButton, gbc); + final JPanel panel5 = new JPanel(); + panel5.setLayout(new GridBagLayout()); + panel5.setToolTipText("status"); + mPanel.add(panel5, cc.xyw(2, 1, 3)); + panel5.setBorder(BorderFactory.createTitledBorder("Status")); mAllStatusRadioButton = new JRadioButton(); mAllStatusRadioButton.setSelected(true); mAllStatusRadioButton.setText("All"); @@ -204,7 +383,7 @@ public ApplicationManagementDialog(@Nullable Project project) { gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; - panel3.add(mAllStatusRadioButton, gbc); + panel5.add(mAllStatusRadioButton, gbc); mEnabledRadioButton = new JRadioButton(); mEnabledRadioButton.setText("enabled"); gbc = new GridBagConstraints(); @@ -213,7 +392,7 @@ public ApplicationManagementDialog(@Nullable Project project) { gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; - panel3.add(mEnabledRadioButton, gbc); + panel5.add(mEnabledRadioButton, gbc); mDisabledRadioButton = new JRadioButton(); mDisabledRadioButton.setText("disabled"); gbc = new GridBagConstraints(); @@ -222,46 +401,11 @@ public ApplicationManagementDialog(@Nullable Project project) { gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.anchor = GridBagConstraints.WEST; - panel3.add(mDisabledRadioButton, gbc); - mQueryButton = new JButton(); - mQueryButton.setText("Query"); - mPanel.add(mQueryButton, cc.xy(5, 3)); - final JPanel panel4 = new JPanel(); - panel4.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); - mPanel.add(panel4, cc.xyw(1, 3, 3)); - final JLabel label1 = new JLabel(); - label1.setText("Name Filter :"); - panel4.add(label1, - new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, - null, 0, false)); - tv_keyword = new JTextField(); - panel4.add(tv_keyword, - new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, - null, new Dimension(150, -1), null, 0, false)); - final JPanel panel5 = new JPanel(); - panel5.setLayout( - new FormLayout("fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:max(d;4px):noGrow", - "center:d:noGrow")); - mPanel.add(panel5, cc.xyw(1, 7, 5)); - mUninstallButton = new JButton(); - mUninstallButton.setText("Uninstall"); - panel5.add(mUninstallButton, cc.xy(1, 1)); - mClearAppCacheDataButton = new JButton(); - mClearAppCacheDataButton.setText("Clear app cache data"); - panel5.add(mClearAppCacheDataButton, cc.xy(3, 1)); - mRunningServicesButton = new JButton(); - mRunningServicesButton.setText("Running Services"); - panel5.add(mRunningServicesButton, cc.xy(5, 1)); - mViewDetailButton = new JButton(); - mViewDetailButton.setText("View Detail"); - panel5.add(mViewDetailButton, cc.xy(7, 1)); - mViewPathButton = new JButton(); - mViewPathButton.setText("View Path"); - panel5.add(mViewPathButton, cc.xy(9, 1)); - sp = new JScrollPane(); - mPanel.add(sp, cc.xyw(1, 5, 5, CellConstraints.FILL, CellConstraints.FILL)); - mList1 = new JList(); - sp.setViewportView(mList1); + panel5.add(mDisabledRadioButton, gbc); + sp_tp = new JScrollPane(); + mPanel.add(sp_tp, cc.xyw(1, 7, 4, CellConstraints.FILL, CellConstraints.FILL)); + tp = new JTextPane(); + sp_tp.setViewportView(tp); ButtonGroup buttonGroup; buttonGroup = new ButtonGroup(); buttonGroup.add(mDisabledRadioButton); diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java b/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java index 21d74a12..f2beb61a 100644 --- a/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceChooserDialog.java @@ -11,15 +11,16 @@ import java.awt.BorderLayout; 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; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.NotNull; import org.joor.Reflect; -import javax.inject.Inject; -import javax.swing.*; -import java.util.stream.Collectors; - import static java.util.Arrays.stream; public class DeviceChooserDialog extends DialogWrapper { diff --git a/src/main/java/com/developerphil/adbidea/ui/NotificationHelper.java b/src/main/java/com/developerphil/adbidea/ui/NotificationHelper.java index ba67c59e..3f5f6a0d 100644 --- a/src/main/java/com/developerphil/adbidea/ui/NotificationHelper.java +++ b/src/main/java/com/developerphil/adbidea/ui/NotificationHelper.java @@ -1,14 +1,15 @@ 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 { - private static final NotificationGroup INFO = NotificationGroup.logOnlyGroup("ADB Idea (Logging)"); - private static final NotificationGroup ERRORS = NotificationGroup.balloonGroup("ADB Idea (Errors)"); - private static final NotificationListener NOOP_LISTENER = (notification, event) -> { + 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) { @@ -20,7 +21,8 @@ public static void error(String message) { } private static void sendNotification(String message, NotificationType notificationType, NotificationGroup notificationGroup) { - notificationGroup.createNotification("ADB IDEA", escapeString(message), notificationType, NOOP_LISTENER).notify(null); + Notification adb_idea = notificationGroup.createNotification("ADB IDEA", escapeString(message), notificationType, NOOP_LISTENER); + adb_idea.notify(null); } diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/ActivityServiceCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/ActivityServiceCommand.kt new file mode 100644 index 00000000..6b6084ab --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/ActivityServiceCommand.kt @@ -0,0 +1,39 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.* +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class ActivityServiceCommand(private val mPackageName: String,private val callback:(String)->Unit) : Command { + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + var packageName = packageName + packageName = mPackageName + try { + if (packageName.isNotEmpty()&&!isAppInstalled(device, packageName)) { + error(String.format("%s is not installed on %s", packageName, device.name)) + return false + } + val receiver = PrintReceiver() + device.executeShellCommand("dumpsys activity services $packageName", receiver, 15L, TimeUnit.SECONDS) + info(String.format("%s get activity service on %s", packageName, device.name)) + val string = receiver.toString() + callback.invoke(string) + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) + notification.notify(project) + return true + } catch (e1: Exception) { + error("get activity service... " + e1.message) + } + + return false + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt new file mode 100644 index 00000000..c20609dc --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt @@ -0,0 +1,39 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.* +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class CommonStringResultCommand(private val mPackageName: String,val commandStr:String,val operationDesc:String,private val callback:(String)->Unit) : Command { + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + var packageName = packageName + packageName = mPackageName + try { + if (isAppInstalled(device, packageName)) { + val receiver = PrintReceiver() + device.executeShellCommand("$commandStr $packageName", receiver, 15L, TimeUnit.SECONDS) + info(String.format("%s $operationDesc on %s", packageName, device.name)) + val string = receiver.toString() + callback.invoke(string) + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) + notification.notify(project) + return true + } else { + error(String.format("%s is not installed on %s", packageName, device.name)) + } + } catch (e1: Exception) { + error("$operationDesc... " + e1.message) + } + + return false + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/PackageDetailCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/PackageDetailCommand.kt new file mode 100644 index 00000000..dce91aa2 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/PackageDetailCommand.kt @@ -0,0 +1,39 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.* +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class PackageDetailCommand(private val mPackageName: String,private val callback:(String)->Unit) : Command { + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + var packageName = packageName + packageName = mPackageName + try { + if (isAppInstalled(device, packageName)) { + val receiver = PrintReceiver() + device.executeShellCommand("dumpsys package $packageName", receiver, 15L, TimeUnit.SECONDS) + info(String.format("%s get package detail on %s", packageName, device.name)) + val string = receiver.toString() + callback.invoke(string) + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) + notification.notify(project) + return true + } else { + error(String.format("%s is not installed on %s", packageName, device.name)) + } + } catch (e1: Exception) { + error("Get package detail... " + e1.message) + } + + return false + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/PackagePathCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/PackagePathCommand.kt new file mode 100644 index 00000000..81b58458 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/PackagePathCommand.kt @@ -0,0 +1,39 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.* +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class PackagePathCommand(private val mPackageName: String,private val callback:(String)->Unit) : Command { + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + var packageName = packageName + packageName = mPackageName + try { + if (isAppInstalled(device, packageName)) { + val receiver = PrintReceiver() + device.executeShellCommand("pm path $packageName", receiver, 15L, TimeUnit.SECONDS) + info(String.format("%s get package path on %s", packageName, device.name)) + val string = receiver.toString() + callback.invoke(string) + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) + notification.notify(project) + return true + } else { + error(String.format("%s is not installed on %s", packageName, device.name)) + } + } catch (e1: Exception) { + error("Get package path... " + e1.message) + } + + return false + } + +} From a7302fe6bcdbec9f35098bf7573e8d659baf6b72 Mon Sep 17 00:00:00 2001 From: longforus Date: Mon, 8 Oct 2018 18:07:50 +0800 Subject: [PATCH 03/43] fix delete app bug,change print receiver --- build.gradle | 1 - .../adb/command/receiver/PrintReceiver.java | 52 +++++++++++-------- .../ui/ApplicationManagementDialog.form | 2 +- .../ui/ApplicationManagementDialog.java | 16 ++++-- .../adbidea/ui/MyApplistModel.java | 36 +++++++++++++ 5 files changed, 78 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/developerphil/adbidea/ui/MyApplistModel.java diff --git a/build.gradle b/build.gradle index f36fa68c..8f0d11e5 100644 --- a/build.gradle +++ b/build.gradle @@ -68,7 +68,6 @@ dependencies { testCompile "org.mockito:mockito-core:1.+" testCompile "com.google.truth:truth:0.30" - } task(verifySetup) { 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 index 561a88ed..5e3ffa83 100644 --- a/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java +++ b/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java @@ -1,31 +1,37 @@ package com.developerphil.adbidea.adb.command.receiver; -import java.util.List; +import com.android.ddmlib.IShellOutputReceiver; +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; + } + -public class PrintReceiver extends GenericReceiver { @Override public String toString() { - List outputLines = getAdbOutputLines(); - if (!outputLines.isEmpty()) { - StringBuilder stringBuilder = new StringBuilder(); - int tabCount = 0; - for (String line : outputLines) { - stringBuilder.append(line); - stringBuilder.append("\n"); - if (line.isEmpty()) { - tabCount = 0; - continue; - } - if (line.endsWith(":")) { - tabCount++; - } - for (int i = 0; i < tabCount; i++) { - stringBuilder.append(" "); - } - } - return stringBuilder.toString(); - } - return super.toString(); + return mString; } } diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form index a547474f..9aa3ae67 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form @@ -13,7 +13,7 @@ - + diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java index 4957b5e4..4080e236 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java @@ -45,6 +45,7 @@ */ public class ApplicationManagementDialog extends JDialog { + private MyApplistModel mModel; private List mList; private JRadioButton mAllStatusRadioButton; private JRadioButton mDisabledRadioButton; @@ -135,17 +136,24 @@ public void mouseClicked(MouseEvent e) { AdbFacade.getAllApplicationList(mProject, sb.toString(), strings -> { Collections.sort(strings); mList = strings; - mJList.setListData(strings.toArray()); + mModel = new MyApplistModel(mList); + mJList.setModel(mModel); return null; }); }); mUninstallButton.addActionListener(e -> { int[] selectedIndices = mJList.getSelectedIndices(); - for (int index : selectedIndices) { - AdbFacade.uninstall(mProject, getRealPackageName(mList.get(index))); - mJList.remove(index); + 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)); + selected[i] = packageName; + } + for (String s : selected) { + mModel.delete(s); } }); + mClearAppCacheDataButton.addActionListener(e -> { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { diff --git a/src/main/java/com/developerphil/adbidea/ui/MyApplistModel.java b/src/main/java/com/developerphil/adbidea/ui/MyApplistModel.java new file mode 100644 index 00000000..cec36ae4 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/MyApplistModel.java @@ -0,0 +1,36 @@ +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); + } + } +} From e8d85d892e6d666479a8d6ecc997635c3cf65617 Mon Sep 17 00:00:00 2001 From: longforus Date: Tue, 9 Oct 2018 15:24:07 +0800 Subject: [PATCH 04/43] add apk file install and process force stop --- .../developerphil/adbidea/adb/AdbFacade.java | 8 ++++ .../ui/ApplicationManagementDialog.form | 15 ++++++- .../ui/ApplicationManagementDialog.java | 13 +++++- .../adbidea/action/extend/InstallApkAction.kt | 40 ++++++++++++++++++ .../adbidea/adb/ForceStopCommand.kt | 41 +++++++++++++++++++ .../adbidea/adb/InstallApkCommand.kt | 28 +++++++++++++ src/main/resources/META-INF/plugin.xml | 16 ++++---- 7 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/ForceStopCommand.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index 79e18c7f..fc98181c 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -16,6 +16,7 @@ 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; @@ -37,6 +38,10 @@ 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()); } @@ -75,6 +80,9 @@ public static void clearData(Project project) { 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)); } diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form index 9aa3ae67..5078725a 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form @@ -7,6 +7,8 @@ + + @@ -18,7 +20,7 @@ - + @@ -270,7 +272,7 @@ - + @@ -289,6 +291,15 @@ + + + + + + + + + diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java index 4080e236..9170dd2d 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java @@ -68,6 +68,7 @@ public class ApplicationManagementDialog extends JDialog { private JScrollPane sp; private JTextPane tp; private JScrollPane sp_tp; + private JButton mForceStopButton; private static final String PARAMETER_DISABLED = "-d "; private static final String PARAMETER_ENABLED = "-e "; @@ -84,8 +85,8 @@ public ApplicationManagementDialog(@Nullable Project project) { setResizable(true); setModal(true); Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); - int x = (int) screensize.getWidth() / 3 - getWidth() / 2; - int y = (int) screensize.getHeight() / 3 - getHeight() / 2; + int x = (int) screensize.getWidth() / 2 - mPanel.getPreferredSize().width / 2; + int y = (int) screensize.getHeight() / 2 - mPanel.getPreferredSize().height / 2; setTitle("Adb Application Management"); setLocation(x, y); mProject = project; @@ -209,6 +210,14 @@ public void mouseClicked(MouseEvent e) { }); } }); + mForceStopButton.addActionListener(e -> { + List selectedValuesList = mJList.getSelectedValuesList(); + for (String packageName : selectedValuesList) { + String name = getRealPackageName(packageName); + append2Ta("Force-stop : " + name + "\n", JBColor.BLUE); + AdbFacade.forceStop(mProject, name); + } + }); setContentPane($$$getRootComponent$$$()); } diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt new file mode 100644 index 00000000..1fdfd602 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/InstallApkAction.kt @@ -0,0 +1,40 @@ +package com.developerphil.adbidea.action.extend + +import com.developerphil.adbidea.action.AdbAction +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.fileChooser.FileChooserDescriptor +import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl +import com.intellij.openapi.project.Project +import org.jdesktop.swingx.util.OS +import java.io.File + + +/** + * Created by XQ Yang on 8/28/2018 2:53 PM. + * Description : + */ +class InstallApkAction : AdbAction() { + + + private lateinit var apkChooserDescriptor: FileChooserDescriptor + + 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" + val apks = FileChooserDialogImpl(apkChooserDescriptor, project) + .choose(project) + if (apks.isNotEmpty()) { + val apkList = mutableListOf() + apks.forEach { + if (it.name.endsWith(".apk")) { + apkList.add(File(it.canonicalPath)) + } + } + if (apkList.isNotEmpty()) { + AdbFacade.installApk(project,apkList) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/ForceStopCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/ForceStopCommand.kt new file mode 100644 index 00000000..9e2fadb2 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/ForceStopCommand.kt @@ -0,0 +1,41 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.* +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +/** + * Created by XQ Yang on 2018-10-9 15:00:43 + * Description : + */ + +class ForceStopCommand(private val mPackageName: String) : Command { + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + try { + 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)) + val string = receiver.toString() + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) + notification.notify(project) + return true + } else { + error(String.format("%s is not installed on %s", mPackageName, device.name)) + } + } catch (e1: Exception) { + error("Force Stop... " + e1.message) + } + + return false + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt new file mode 100644 index 00000000..3ea2a4a2 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt @@ -0,0 +1,28 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.android.ddmlib.InstallException +import com.developerphil.adbidea.adb.command.Command +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.io.File +import java.util.concurrent.TimeUnit + +/** + * Created by XQ Yang on 2018-10-9 15:01:03 + * Description : + */ + +class InstallApkCommand(val apks: MutableList) : Command { + override fun run(project: Project, device: IDevice, facet: AndroidFacet, pn: String?): Boolean { + try { + device.installPackages(apks, true, listOf(), 15, TimeUnit.SECONDS) + info(String.format("Install %d apk file to %s", apks.size, device.name)) + } catch (e1: InstallException) { + error("Install fail... " + e1.message) + } + return false + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index f5a8d4cb..3c002edf 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -194,16 +194,16 @@ text="ADB Restart App With Debugger" description="Restarts the current application and attach the debugger"> - - - + + class="com.developerphil.adbidea.action.extend.ApplicationManagementPopupAction" text="ADB Application management Popup..." + description="List all the adb idea application management operations in a popup"> - - - + + + From d4834d07d075a95c70c01b1338acbe431b2ace4e Mon Sep 17 00:00:00 2001 From: longforus Date: Tue, 9 Oct 2018 16:59:03 +0800 Subject: [PATCH 05/43] add show foreground Activity --- .../developerphil/adbidea/adb/AdbFacade.java | 4 + ...g.form => ApplicationManagementFrame.form} | 72 +++++++++++------- ...g.java => ApplicationManagementFrame.java} | 50 +++++++++--- .../ApplicationManagementPopupAction.kt | 4 +- .../adbidea/adb/ForegroundActivityCommand.kt | 31 ++++++++ src/main/resources/icon.png | Bin 0 -> 33577 bytes 6 files changed, 123 insertions(+), 38 deletions(-) rename src/main/java/com/developerphil/adbidea/ui/{ApplicationManagementDialog.form => ApplicationManagementFrame.form} (86%) rename src/main/java/com/developerphil/adbidea/ui/{ApplicationManagementDialog.java => ApplicationManagementFrame.java} (90%) create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/ForegroundActivityCommand.kt create mode 100644 src/main/resources/icon.png diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index fc98181c..36f1092d 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -122,4 +122,8 @@ private static void executeOnDevice(final Project project, final Command runnabl 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)); + } } diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form similarity index 86% rename from src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form rename to src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form index 5078725a..f0d0718a 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.form +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form @@ -1,15 +1,14 @@ - + - + + - - @@ -20,7 +19,7 @@ - + @@ -100,16 +99,14 @@ - + + + - + - - - - @@ -119,7 +116,9 @@ - + + + @@ -128,7 +127,9 @@ - + + + @@ -137,7 +138,9 @@ - + + + @@ -146,7 +149,9 @@ - + + + @@ -155,13 +160,37 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -272,7 +301,7 @@ - + @@ -291,15 +320,6 @@ - - - - - - - - - diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java similarity index 90% rename from src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java rename to src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java index 9170dd2d..bfa6d9bf 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java @@ -14,14 +14,16 @@ import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.MouseEvent; +import java.net.URL; import java.util.Collections; import java.util.List; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; -import javax.swing.JDialog; +import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; @@ -44,7 +46,7 @@ * Description : */ -public class ApplicationManagementDialog extends JDialog { +public class ApplicationManagementFrame extends JFrame { private MyApplistModel mModel; private List mList; private JRadioButton mAllStatusRadioButton; @@ -69,6 +71,7 @@ public class ApplicationManagementDialog extends JDialog { private JTextPane tp; private JScrollPane sp_tp; private JButton mForceStopButton; + private JButton mForegroundActivityButton; private static final String PARAMETER_DISABLED = "-d "; private static final String PARAMETER_ENABLED = "-e "; @@ -81,13 +84,15 @@ public class ApplicationManagementDialog extends JDialog { private Project mProject; private final JPopupMenu mPopupMenu; - public ApplicationManagementDialog(@Nullable Project project) { + public ApplicationManagementFrame(@Nullable Project project) { setResizable(true); - setModal(true); Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); int x = (int) screensize.getWidth() / 2 - mPanel.getPreferredSize().width / 2; int y = (int) screensize.getHeight() / 2 - mPanel.getPreferredSize().height / 2; setTitle("Adb Application Management"); + URL filename = getClass().getResource("/icon.png"); + ImageIcon icon = new ImageIcon(filename); + setIconImage(icon.getImage()); setLocation(x, y); mProject = project; mPopupMenu = new JPopupMenu(); @@ -218,6 +223,13 @@ public void mouseClicked(MouseEvent e) { AdbFacade.forceStop(mProject, name); } }); + mForegroundActivityButton.addActionListener(e -> { + append2Ta("Foreground Activity : \n", JBColor.BLUE); + AdbFacade.showForegroundActivity(mProject, s -> { + append2Ta(s); + return null; + }); + }); setContentPane($$$getRootComponent$$$()); } @@ -258,6 +270,12 @@ private String getRealPackageName(String packageName) { return strings[1]; } + public static void main(String... args) { + ApplicationManagementFrame frame = new ApplicationManagementFrame(null); + frame.pack(); + frame.setVisible(true); + } + { // GUI initializer generated by IntelliJ IDEA GUI Designer // >>> IMPORTANT!! <<< @@ -275,10 +293,10 @@ private String getRealPackageName(String packageName) { private void $$$setupUI$$$() { mPanel = new JPanel(); mPanel.setLayout(new FormLayout("fill:341px:noGrow,fill:d:grow,fill:d:noGrow,fill:d:noGrow", - "center:max(d;4px):noGrow,center:44px:noGrow,center:32px:noGrow,center:d:noGrow,center:32px:noGrow,top:4dlu:noGrow,center:d:grow")); + "center:max(d;4px):noGrow,center:44px:noGrow,center:32px:noGrow,center:d:noGrow,center:p:noGrow,center:d:grow")); mPanel.setMinimumSize(new Dimension(590, 280)); mPanel.setName(""); - mPanel.setPreferredSize(new Dimension(590, 280)); + mPanel.setPreferredSize(new Dimension(690, 580)); mPanel.setBorder(BorderFactory.createTitledBorder("All Application")); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridBagLayout()); @@ -329,9 +347,10 @@ private String getRealPackageName(String packageName) { new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); final JPanel panel3 = new JPanel(); - panel3.setLayout( - new FormLayout("fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:max(d;4px):noGrow", - "center:d:noGrow")); + panel3.setLayout(new FormLayout( + "fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow," + + "left:4dlu:noGrow,fill:d:grow", + "center:d:grow")); mPanel.add(panel3, cc.xyw(1, 5, 4)); mUninstallButton = new JButton(); mUninstallButton.setText("Uninstall"); @@ -348,6 +367,12 @@ private String getRealPackageName(String packageName) { mViewPathButton = new JButton(); mViewPathButton.setText("View Path"); panel3.add(mViewPathButton, cc.xy(9, 1)); + mForceStopButton = new JButton(); + mForceStopButton.setText("Force stop"); + panel3.add(mForceStopButton, cc.xy(11, 1)); + mForegroundActivityButton = new JButton(); + mForegroundActivityButton.setText("Foreground Activity"); + panel3.add(mForegroundActivityButton, cc.xy(13, 1)); sp = new JScrollPane(); sp.setMinimumSize(new Dimension(0, 100)); mPanel.add(sp, cc.xyw(1, 4, 4, CellConstraints.FILL, CellConstraints.FILL)); @@ -420,8 +445,13 @@ private String getRealPackageName(String packageName) { gbc.anchor = GridBagConstraints.WEST; panel5.add(mDisabledRadioButton, gbc); sp_tp = new JScrollPane(); - mPanel.add(sp_tp, cc.xyw(1, 7, 4, CellConstraints.FILL, CellConstraints.FILL)); + sp_tp.setPreferredSize(new Dimension(590, 200)); + sp_tp.setVisible(true); + sp_tp.setWheelScrollingEnabled(true); + mPanel.add(sp_tp, cc.xyw(1, 6, 4, CellConstraints.FILL, CellConstraints.FILL)); tp = new JTextPane(); + tp.setPreferredSize(new Dimension(590, 200)); + tp.setVisible(true); sp_tp.setViewportView(tp); ButtonGroup buttonGroup; buttonGroup = new ButtonGroup(); 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 470e23f9..18d46c58 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt @@ -1,7 +1,7 @@ package com.developerphil.adbidea.action.extend import com.developerphil.adbidea.action.AdbAction -import com.developerphil.adbidea.ui.ApplicationManagementDialog +import com.developerphil.adbidea.ui.ApplicationManagementFrame import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.Project @@ -13,7 +13,7 @@ class ApplicationManagementPopupAction:AdbAction(){ override fun actionPerformed(e: AnActionEvent?, project: Project?) { - val popup = ApplicationManagementDialog(project) + val popup = ApplicationManagementFrame(project) popup.pack() popup.isVisible = true } diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/ForegroundActivityCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/ForegroundActivityCommand.kt new file mode 100644 index 00000000..f1c5d76b --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/ForegroundActivityCommand.kt @@ -0,0 +1,31 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.* +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class ForegroundActivityCommand(private val callback:(String)->Unit) : Command { + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + 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)) + val string = receiver.toString() + callback.invoke(string) + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) + notification.notify(project) + return true + } catch (e1: Exception) { + error("Get foreground Activity... " + e1.message) + } + return false + } + +} diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..321ce39001f513793dd946823fadf0e3bc60b99a GIT binary patch literal 33577 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 Date: Wed, 10 Oct 2018 11:15:19 +0800 Subject: [PATCH 06/43] add string put --- .../developerphil/adbidea/adb/AdbFacade.java | 6 ++++ .../ui/ApplicationManagementFrame.form | 9 +++++ .../ui/ApplicationManagementFrame.java | 33 ++++++++++++------- .../adbidea/action/extend/PutStringAction.kt | 25 ++++++++++++++ .../adbidea/adb/PutStringToDeviceCommand.kt | 30 +++++++++++++++++ src/main/resources/META-INF/plugin.xml | 4 +++ 6 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/PutStringToDeviceCommand.kt diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index 36f1092d..5f214307 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -22,6 +22,8 @@ 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; @@ -126,4 +128,8 @@ public static void clearData(Project project, String realPackageName) { public static void showForegroundActivity(Project project,Function1 callback) { executeOnDevice(project, new ForegroundActivityCommand(callback)); } + + public static void putStringToDevice(@Nullable Project project, @NotNull String str) { + executeOnDevice(project, new PutStringToDeviceCommand(str)); + } } diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form index f0d0718a..2852edb4 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form @@ -191,6 +191,15 @@ + + + + + + + + + diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java index bfa6d9bf..c13f1baf 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java @@ -72,6 +72,7 @@ public class ApplicationManagementFrame extends JFrame { private JScrollPane sp_tp; private JButton mForceStopButton; private JButton mForegroundActivityButton; + private JButton mTrimMemoryButton; private static final String PARAMETER_DISABLED = "-d "; private static final String PARAMETER_ENABLED = "-e "; @@ -230,6 +231,14 @@ public void mouseClicked(MouseEvent e) { return null; }); }); + //mTrimMemoryButton.addActionListener(e -> { + // List selectedValuesList = mJList.getSelectedValuesList(); + // for (String packageName : selectedValuesList) { + // String name = getRealPackageName(packageName); + // append2Ta("Trim Memory of " + name + ":\n", JBColor.BLUE); + // AdbFacade.trimMemory(mProject, name); + // } + //}); setContentPane($$$getRootComponent$$$()); } @@ -293,10 +302,10 @@ public static void main(String... args) { private void $$$setupUI$$$() { mPanel = new JPanel(); mPanel.setLayout(new FormLayout("fill:341px:noGrow,fill:d:grow,fill:d:noGrow,fill:d:noGrow", - "center:max(d;4px):noGrow,center:44px:noGrow,center:32px:noGrow,center:d:noGrow,center:p:noGrow,center:d:grow")); + "center:max(d;4px):noGrow,center:44px:noGrow,center:32px:noGrow,center:d:noGrow,center:p:noGrow,center:d:grow,top:4dlu:noGrow,center:max(d;4px):noGrow")); mPanel.setMinimumSize(new Dimension(590, 280)); mPanel.setName(""); - mPanel.setPreferredSize(new Dimension(690, 580)); + mPanel.setPreferredSize(new Dimension(800, 620)); mPanel.setBorder(BorderFactory.createTitledBorder("All Application")); final JPanel panel1 = new JPanel(); panel1.setLayout(new GridBagLayout()); @@ -347,32 +356,32 @@ public static void main(String... args) { new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); final JPanel panel3 = new JPanel(); - panel3.setLayout(new FormLayout( - "fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow,left:4dlu:noGrow,fill:d:grow," + - "left:4dlu:noGrow,fill:d:grow", - "center:d:grow")); + panel3.setLayout(new FormLayout("fill:170px:grow,fill:d:grow,fill:153px:grow,fill:d:grow,fill:d:grow,fill:d:grow", "center:30px:grow,center:max(d;4px):noGrow")); mPanel.add(panel3, cc.xyw(1, 5, 4)); mUninstallButton = new JButton(); mUninstallButton.setText("Uninstall"); panel3.add(mUninstallButton, cc.xy(1, 1)); mClearAppCacheDataButton = new JButton(); mClearAppCacheDataButton.setText("Clear app cache data"); - panel3.add(mClearAppCacheDataButton, cc.xy(3, 1)); + panel3.add(mClearAppCacheDataButton, cc.xy(2, 1)); mRunningServicesButton = new JButton(); mRunningServicesButton.setText("Running Services"); - panel3.add(mRunningServicesButton, cc.xy(5, 1)); + panel3.add(mRunningServicesButton, cc.xy(3, 1)); mViewDetailButton = new JButton(); mViewDetailButton.setText("View Detail"); - panel3.add(mViewDetailButton, cc.xy(7, 1)); + panel3.add(mViewDetailButton, cc.xy(4, 1)); mViewPathButton = new JButton(); mViewPathButton.setText("View Path"); - panel3.add(mViewPathButton, cc.xy(9, 1)); + panel3.add(mViewPathButton, cc.xy(5, 1)); mForceStopButton = new JButton(); mForceStopButton.setText("Force stop"); - panel3.add(mForceStopButton, cc.xy(11, 1)); + panel3.add(mForceStopButton, cc.xy(6, 1)); mForegroundActivityButton = new JButton(); mForegroundActivityButton.setText("Foreground Activity"); - panel3.add(mForegroundActivityButton, cc.xy(13, 1)); + panel3.add(mForegroundActivityButton, cc.xy(1, 2)); + mTrimMemoryButton = new JButton(); + mTrimMemoryButton.setText("Trim memory"); + panel3.add(mTrimMemoryButton, cc.xy(2, 2)); sp = new JScrollPane(); sp.setMinimumSize(new Dimension(0, 100)); mPanel.add(sp, cc.xyw(1, 4, 4, CellConstraints.FILL, CellConstraints.FILL)); diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt new file mode 100644 index 00000000..704ad52e --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt @@ -0,0 +1,25 @@ +package com.developerphil.adbidea.action.extend + +import com.developerphil.adbidea.action.AdbAction +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project +import javax.swing.JOptionPane + + +/** + * Created by XQ Yang on 8/28/2018 2:53 PM. + * Description : input simple string to device + */ +class PutStringAction : AdbAction() { + + + override fun actionPerformed(e: AnActionEvent?, project: Project?) { + var result = JOptionPane.showInputDialog("Input simple string put to device") + if (!result.isNullOrEmpty()) { + result = result.replace(" ","") + AdbFacade.putStringToDevice(project,result) + } + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/PutStringToDeviceCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/PutStringToDeviceCommand.kt new file mode 100644 index 00000000..b23db8ba --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/PutStringToDeviceCommand.kt @@ -0,0 +1,30 @@ +package com.developerphil.adbidea.adb + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.adb.command.Command +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +/** + * Created by XQ Yang on 10/10/2018 10:56 AM. + * Description : + */ +class PutStringToDeviceCommand(val str:String):Command{ + 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()) + } + return true + } catch (e1: Exception) { + NotificationHelper.error("Put String to device... " + e1.message) + } + return false + } + +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 3c002edf..8ef96661 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -203,6 +203,10 @@ class="com.developerphil.adbidea.action.extend.InstallApkAction" text="ADB Install Apk file" description="Install Apk file to phone"> + + From 773d3eb2b8573051b7917403bb7baffa85dc9b51 Mon Sep 17 00:00:00 2001 From: longforus Date: Wed, 10 Oct 2018 18:16:44 +0800 Subject: [PATCH 07/43] add interacting from --- .../developerphil/adbidea/adb/AdbFacade.java | 36 +-- .../adbidea/ui/BoundTableModel.java | 116 ++++++++ .../adbidea/ui/BoundTypeCellEditor.java | 17 ++ .../adbidea/ui/BoundTypeJTable.java | 23 ++ .../adbidea/ui/InteractingForm.form | 253 ++++++++++++++++++ .../adbidea/ui/InteractingForm.java | 124 +++++++++ .../com/developerphil/adbidea/ui/Utils.java | 8 +- .../kotlin/com/developerphil/adbidea/Const.kt | 115 ++++++++ .../developerphil/adbidea/HelperMethods.kt | 5 + .../ApplicationManagementPopupAction.kt | 6 +- .../action/extend/InteractingAction.kt | 22 ++ .../adbidea/adb/CommonStringResultCommand.kt | 24 +- .../adbidea/adb/InstallApkCommand.kt | 1 + .../adbidea/adb/InteractingCommand.kt | 39 +++ .../adbidea/bean/BoundItemBean.kt | 34 +++ src/main/resources/META-INF/plugin.xml | 4 + 16 files changed, 787 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java create mode 100644 src/main/java/com/developerphil/adbidea/ui/BoundTypeCellEditor.java create mode 100644 src/main/java/com/developerphil/adbidea/ui/BoundTypeJTable.java create mode 100644 src/main/java/com/developerphil/adbidea/ui/InteractingForm.form create mode 100644 src/main/java/com/developerphil/adbidea/ui/InteractingForm.java create mode 100644 src/main/kotlin/com/developerphil/adbidea/Const.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/InteractingCommand.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index 5f214307..59b1ec20 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -13,6 +13,7 @@ import com.developerphil.adbidea.adb.command.RevokePermissionsCommand; 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; @@ -40,7 +41,7 @@ public static void uninstall(Project project) { executeOnDevice(project, new UninstallCommand()); } - public static void installApk(Project project,List apks) { + public static void installApk(Project project, List apks) { executeOnDevice(project, new InstallApkCommand(apks)); } @@ -79,26 +80,29 @@ public static void restartDefaultActivityWithDebugger(Project project) { 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 getPackageDetail(Project project, String packageName, Function1 callback) { + executeOnDevice(project, new PackageDetailCommand(packageName, callback)); } - public static void forceStop(Project project,String packageName) { + + 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 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 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)); + 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) { @@ -108,9 +112,7 @@ private static void executeOnDevice(final Project project, final Command runnabl return; } - final DeviceResult result = project.getComponent(ObjectGraph.class) - .getDeviceResultFetcher() - .fetch(); + final DeviceResult result = project.getComponent(ObjectGraph.class).getDeviceResultFetcher().fetch(); if (result != null) { for (final IDevice device : result.getDevices()) { @@ -125,11 +127,15 @@ public static void clearData(Project project, String realPackageName) { executeOnDevice(project, new ClearDataCommand(realPackageName)); } - public static void showForegroundActivity(Project project,Function1 callback) { + public static void showForegroundActivity(Project project, Function1 callback) { executeOnDevice(project, new ForegroundActivityCommand(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)); + } } diff --git a/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java b/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java new file mode 100644 index 00000000..3889ef14 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java @@ -0,0 +1,116 @@ +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.NULL)); + 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 new file mode 100644 index 00000000..c3b6256f --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/BoundTypeCellEditor.java @@ -0,0 +1,17 @@ +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 new file mode 100644 index 00000000..16af95c9 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/BoundTypeJTable.java @@ -0,0 +1,23 @@ +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/InteractingForm.form b/src/main/java/com/developerphil/adbidea/ui/InteractingForm.form new file mode 100644 index 00000000..9e007f95 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/InteractingForm.form @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/developerphil/adbidea/ui/InteractingForm.java b/src/main/java/com/developerphil/adbidea/ui/InteractingForm.java new file mode 100644 index 00000000..accc095b --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/InteractingForm.java @@ -0,0 +1,124 @@ +package com.developerphil.adbidea.ui; + +import com.developerphil.adbidea.ConstKt; +import com.developerphil.adbidea.HelperMethodsKt; +import com.developerphil.adbidea.adb.AdbFacade; +import com.developerphil.adbidea.bean.BoundItemBean; +import com.intellij.openapi.project.Project; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import javax.swing.DefaultComboBoxModel; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import org.jetbrains.annotations.Nullable; + +/** + * Created by XQ Yang on 10/10/2018 11:25 AM. + * Description : + */ + +public class InteractingForm extends JFrame { + + private final Project mProject; + private JPanel mPanel; + private JRadioButton mStartActivityRadioButton; + private JRadioButton mStartServiceRadioButton; + private JRadioButton mSendBroadcastRadioButton; + private JComboBox mCbAction; + private JComboBox mCbCategory; + private JComboBox mCbComponent; + private BoundTypeJTable mTable; + private JButton mGoButton; + private JButton mAddRowButton; + private JButton mDeleteRowButton; + private JButton mClearButton; + private BoundTableModel mModel; + + public InteractingForm(@Nullable Project project) { + setResizable(true); + Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); + int x = (int) screensize.getWidth() / 2 - mPanel.getPreferredSize().width / 2; + int y = (int) screensize.getHeight() / 2 - mPanel.getPreferredSize().height / 2; + setTitle("Adb Interacting with applications"); + URL filename = getClass().getResource("/icon.png"); + ImageIcon icon = new ImageIcon(filename); + setIconImage(icon.getImage()); + setLocation(x, y); + mProject = project; + + mCbAction.setModel(new DefaultComboBoxModel(ConstKt.getStartActivityActions())); + mCbCategory.setModel(new DefaultComboBoxModel(ConstKt.getCategorys())); + + mStartActivityRadioButton.addActionListener(e -> mCbAction.setModel(new DefaultComboBoxModel(ConstKt.getStartActivityActions()))); + mSendBroadcastRadioButton.addActionListener(e -> mCbAction.setModel(new DefaultComboBoxModel(ConstKt.getBroadCastActions()))); + + boundInit(); + mGoButton.addActionListener(e -> { + Object selectedItem = mCbComponent.getSelectedItem(); + String name = selectedItem != null ? selectedItem.toString() : ""; + if (!mSendBroadcastRadioButton.isSelected() && Utils.isEmpty(name)) { + HelperMethodsKt.showErrorMsg("Component name cannot be empty"); + return; + } + selectedItem = mCbAction.getSelectedItem(); + String action = selectedItem != null ? selectedItem.toString() : ""; + selectedItem = mCbCategory.getSelectedItem(); + 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); + }); + + setContentPane(mPanel); + } + + public void boundInit() { + mModel = new BoundTableModel(); + mTable.setModel(mModel); + mTable.getColumnModel().getColumn(0).setPreferredWidth(50); + mTable.getColumnModel().getColumn(1).setPreferredWidth(120); + mTable.getColumnModel().getColumn(2).setPreferredWidth(250); + mTable.getColumnModel().getColumn(3).setPreferredWidth(70); + mTable.setRowHeight(25); + mTable.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_DELETE) { + mModel.removeRow(mTable.getSelectedRow()); + } + } + }); + mAddRowButton.addActionListener(e -> { + mModel.addEmptyRow(); + mTable.requestFocus(); + int index = mModel.getRowCount() - 1; + mTable.setRowSelectionInterval(index, index); + mTable.editCellAt(index, 1); + }); + mDeleteRowButton.addActionListener(e -> mModel.removeRow(mTable.getSelectedRow())); + mClearButton.addActionListener(e -> mModel.setData(new ArrayList<>())); + } + + private void createUIComponents() { + mTable = new BoundTypeJTable(); + } +} diff --git a/src/main/java/com/developerphil/adbidea/ui/Utils.java b/src/main/java/com/developerphil/adbidea/ui/Utils.java index cc70ec91..7ffea696 100644 --- a/src/main/java/com/developerphil/adbidea/ui/Utils.java +++ b/src/main/java/com/developerphil/adbidea/ui/Utils.java @@ -9,11 +9,7 @@ public class Utils { public Utils() { } - public static boolean isEmpty(CharSequence s) { - if (s == null) { - return true; - } else { - return s.length() == 0; - } + public static boolean isEmpty(CharSequence str) { + return str == null || str.length() == 0; } } diff --git a/src/main/kotlin/com/developerphil/adbidea/Const.kt b/src/main/kotlin/com/developerphil/adbidea/Const.kt new file mode 100644 index 00000000..2b450d2b --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/Const.kt @@ -0,0 +1,115 @@ +package com.developerphil.adbidea + +/** + * Created by XQ Yang on 10/10/2018 3:28 PM. + * Description : + */ + +val ACTION_MAIN = "android.intent.action.MAIN" +val ACTION_VIEW = "android.intent.action.VIEW" +val ACTION_ATTACH_DATA = "android.intent.action.ATTACH_DATA" +val ACTION_EDIT = "android.intent.action.EDIT" +val ACTION_PICK = "android.intent.action.PICK" +val ACTION_CHOOSER = "android.intent.action.CHOOSER" +val ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT" +val ACTION_DIAL = "android.intent.action.DIAL" +val ACTION_CALL = "android.intent.action.CALL" +val ACTION_SEND = "android.intent.action.SEND" +val ACTION_SENDTO = "android.intent.action.SENDTO" +val ACTION_ANSWER = "android.intent.action.ANSWER" +val ACTION_INSERT = "android.intent.action.INSERT" +val ACTION_DELETE = "android.intent.action.DELETE" +val ACTION_RUN = "android.intent.action.RUN" +val ACTION_SYNC = "android.intent.action.SYNC" +val ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY" +val ACTION_SEARCH = "android.intent.action.SEARCH" +val ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH" +val ACTION_FACTORY_TEST = "android.intent.action.FACTORY_TEST" + + +val StartActivityActions = arrayOf("", ACTION_MAIN, + ACTION_VIEW, + ACTION_ATTACH_DATA, + ACTION_EDIT, + ACTION_PICK, + ACTION_CHOOSER, + ACTION_GET_CONTENT, + ACTION_DIAL, + ACTION_CALL, + ACTION_SEND, + ACTION_SENDTO, + ACTION_ANSWER, + ACTION_INSERT, + ACTION_DELETE, + ACTION_RUN, + ACTION_SYNC, + ACTION_PICK_ACTIVITY, + ACTION_SEARCH, + ACTION_WEB_SEARCH, + ACTION_FACTORY_TEST +) + + +val CATEGORY_DEFAULT = "android.intent.category.DEFAULT" +val CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE" +val CATEGORY_TAB = "android.intent.category.TAB" +val CATEGORY_ALTERNATIVE = "android.intent.category.ALTERNATIVE" +val CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE" +val CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER" +val CATEGORY_INFO = "android.intent.category.INFO" +val CATEGORY_HOME = "android.intent.category.HOME" +val CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE" +val CATEGORY_TEST = "android.intent.category.TEST" +val CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK" +val CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK" +val CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK" +val CATEGORY_HE_DESK_DOCK = "android.intent.category.HE_DESK_DOCK" +val CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE" +val CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET" +val CATEGORY_VR_HOME = "android.intent.category.VR_HOME" + + +val Categorys = arrayOf("", + CATEGORY_DEFAULT, + CATEGORY_BROWSABLE, + CATEGORY_TAB, + CATEGORY_ALTERNATIVE, + CATEGORY_SELECTED_ALTERNATIVE, + CATEGORY_LAUNCHER, + CATEGORY_INFO, + CATEGORY_HOME, + CATEGORY_PREFERENCE, + CATEGORY_TEST, + CATEGORY_CAR_DOCK, + CATEGORY_DESK_DOCK, + CATEGORY_LE_DESK_DOCK, + CATEGORY_HE_DESK_DOCK, + CATEGORY_CAR_MODE, + CATEGORY_APP_MARKET, + CATEGORY_VR_HOME +) + +val BroadCastActions = arrayOf("", + "android.net.conn.CONNECTIVITY_CHANGE",//网络连接发生变化 + "android.intent.action.SCREEN_ON",//屏幕点亮 + "android.intent.action.SCREEN_OFF",//屏幕熄灭 + "android.intent.action.BATTERY_LOW",//电量低,会弹出电量低提示框 + "android.intent.action.BATTERY_OKAY",//电量恢复了 + "android.intent.action.BOOT_COMPLETED",//设备启动完毕 + "android.intent.action.DEVICE_STORAGE_LOW",//存储空间过低 + "android.intent.action.DEVICE_STORAGE_OK",//存储空间恢复 + "android.intent.action.PACKAGE_ADDED",//安装了新的应用 + "android.net.wifi.STATE_CHANGE",//WiFi连接状态发生变化 + "android.net.wifi.WIFI_STATE_CHANGED",//WiFi状态变为启用/关闭/正在启动/正在关闭/未知 + "android.intent.action.BATTERY_CHANGED",//电池电量发生变化 + "android.intent.action.INPUT_METHOD_CHANGED",//系统输入法发生变化 + "android.intent.action.ACTION_POWER_CONNECTED",//外部电源连接 + "android.intent.action.ACTION_POWER_DISCONNECTED",//外部电源断开连接 + "android.intent.action.DREAMING_STARTED",//系统开始休眠 + "android.intent.action.DREAMING_STOPPED",//系统停止休眠 + "android.intent.action.WALLPAPER_CHANGED",//壁纸发生变化 + "android.intent.action.HEADSET_PLUG",//插入耳机 + "android.intent.action.MEDIA_UNMOUNTED",//卸载外部介质 + "android.intent.action.MEDIA_MOUNTED",//挂载外部介质 + "android.os.action.POWER_SAVE_MODE_CHANGED"//省电模式开启 +) \ No newline at end of file diff --git a/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt b/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt index 2cd9c309..34d650d2 100644 --- a/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt +++ b/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt @@ -1,6 +1,7 @@ package com.developerphil.adbidea import com.intellij.openapi.application.ApplicationManager +import javax.swing.JOptionPane fun waitUntil(timeoutMillis: Long = 30000L, step: Long = 100L, condition: () -> Boolean) { val endTime = System.currentTimeMillis() + timeoutMillis @@ -14,4 +15,8 @@ fun waitUntil(timeoutMillis: Long = 30000L, step: Long = 100L, condition: () -> fun invokeLater(runnable: () -> Unit) { ApplicationManager.getApplication().invokeLater(runnable) +} + +fun showErrorMsg(msg:String){ + JOptionPane.showMessageDialog(null, msg,"Error", JOptionPane.ERROR_MESSAGE) } \ 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 18d46c58..5febff7c 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ApplicationManagementPopupAction.kt @@ -13,9 +13,9 @@ class ApplicationManagementPopupAction:AdbAction(){ override fun actionPerformed(e: AnActionEvent?, project: Project?) { - val popup = ApplicationManagementFrame(project) - popup.pack() - popup.isVisible = true + val form = ApplicationManagementFrame(project) + form.pack() + form.isVisible = true } diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt new file mode 100644 index 00000000..0bba65e4 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt @@ -0,0 +1,22 @@ +package com.developerphil.adbidea.action.extend + +import com.developerphil.adbidea.action.AdbAction +import com.developerphil.adbidea.ui.InteractingForm +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + + +/** + * Created by XQ Yang on 8/28/2018 2:53 PM. + * Description : input simple string to device + */ +class InteractingAction : AdbAction() { + + + override fun actionPerformed(e: AnActionEvent?, project: Project?) { + val form = InteractingForm(project) + form.pack() + form.isVisible = true + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt index c20609dc..0606810d 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt @@ -1,7 +1,6 @@ package com.developerphil.adbidea.adb import com.android.ddmlib.IDevice -import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper @@ -11,24 +10,17 @@ import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet import java.util.concurrent.TimeUnit -class CommonStringResultCommand(private val mPackageName: String,val commandStr:String,val operationDesc:String,private val callback:(String)->Unit) : Command { +class CommonStringResultCommand(val commandStr:String,val operationDesc:String) : Command { override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { - var packageName = packageName - packageName = mPackageName try { - if (isAppInstalled(device, packageName)) { - val receiver = PrintReceiver() - device.executeShellCommand("$commandStr $packageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s $operationDesc on %s", packageName, device.name)) - val string = receiver.toString() - callback.invoke(string) - val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) - notification.notify(project) - return true - } else { - error(String.format("%s is not installed on %s", packageName, device.name)) - } + val receiver = PrintReceiver() + device.executeShellCommand(commandStr, receiver, 15L, TimeUnit.SECONDS) + 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) + return true } catch (e1: Exception) { error("$operationDesc... " + e1.message) } diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt index 3ea2a4a2..121167f0 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt @@ -20,6 +20,7 @@ class InstallApkCommand(val apks: MutableList) : Command { try { device.installPackages(apks, true, listOf(), 15, TimeUnit.SECONDS) info(String.format("Install %d apk file to %s", apks.size, device.name)) + return true } catch (e1: InstallException) { error("Install fail... " + e1.message) } diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/InteractingCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/InteractingCommand.kt new file mode 100644 index 00000000..a90ed59d --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/InteractingCommand.kt @@ -0,0 +1,39 @@ +package com.developerphil.adbidea.adb + +import com.developerphil.adbidea.bean.BoundItemBean + +/** + * Created by XQ Yang on 10/10/2018 4:53 PM. + * Description : + */ + +fun getInteractingCommand(type: Int, action: String, category: String, name: String, + boundData: MutableList): CommonStringResultCommand { + + var desc = "StartActivity" + val sb = StringBuilder(when (type) { + 0 -> "am start " + 1 -> { + desc = "StartService" + "am startservice " + } + 2 -> { + desc = "SendBroadCast" + "am broadcast " + } + else -> "am start " + }) + if (action.isNotEmpty()) { + sb.append("-a $action ") + } + if (category.isNotEmpty()) { + sb.append("-c $category ") + } + if (name.isNotEmpty()) { + sb.append("-n $name ") + } + boundData.forEach { + sb.append(it.toString()) + } + return CommonStringResultCommand(sb.toString(), desc) +} diff --git a/src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt b/src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt new file mode 100644 index 00000000..3bb53c9f --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt @@ -0,0 +1,34 @@ +package com.developerphil.adbidea.bean + +/** + * Created by XQ Yang on 9/4/2018 2:41 PM. + * Description : + */ + +data class BoundItemBean(var selected: Boolean, var key: String, var value: String,var dataType:BoundDataType = BoundDataType.STRING){ + override fun toString(): String { + if (selected&&key.isNotEmpty()) { + if (dataType == BoundDataType.NULL) { + return "${dataType.prefix} \"$key\" " + } + return "${dataType.prefix} \"$key\" \"$value\" " + } + return "" + } +} + + + + +enum class BoundDataType(val prefix:String){ + STRING("--es"), + BOOLEAN("--ez"), + INTEGER("--ei"), + LONG ("--el"), + FLOAT ("--ef"), + URI("--eu"), + COMPONENT_NAME("--ecn"), + INTEGER_ARRAY("--eia"), + LONG_ARRAY("--ela"), + NULL("--esn") +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 8ef96661..148203bb 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -199,6 +199,10 @@ class="com.developerphil.adbidea.action.extend.ApplicationManagementPopupAction" text="ADB Application management Popup..." description="List all the adb idea application management operations in a popup"> + + From f3748a03b5e3ffa4c2030c1f736ed14f737f2411 Mon Sep 17 00:00:00 2001 From: longforus Date: Thu, 11 Oct 2018 16:14:25 +0800 Subject: [PATCH 08/43] add device information frame --- .../developerphil/adbidea/adb/AdbFacade.java | 14 ++ .../ui/ApplicationManagementFrame.java | 53 ++---- .../adbidea/ui/DeviceInfoFrame.form | 130 +++++++++++++++ .../adbidea/ui/DeviceInfoFrame.java | 153 ++++++++++++++++++ ...ractingForm.form => InteractingFrame.form} | 2 +- ...ractingForm.java => InteractingFrame.java} | 4 +- .../com/developerphil/adbidea/ui/Utils.java | 36 +++++ .../action/extend/InteractingAction.kt | 4 +- .../action/extend/ShowDeviceInfoAction.kt | 22 +++ .../{ => command}/ActivityServiceCommand.kt | 3 +- .../CommonStringResultCommand.kt | 17 +- .../adb/{ => command}/ForceStopCommand.kt | 3 +- .../ForegroundActivityCommand.kt | 3 +- .../GetApplicationListCommand.kt | 3 +- .../adb/{ => command}/InstallApkCommand.kt | 3 +- .../adb/{ => command}/InteractingCommand.kt | 2 +- .../adb/{ => command}/PackageDetailCommand.kt | 3 +- .../adb/{ => command}/PackagePathCommand.kt | 3 +- .../{ => command}/PutStringToDeviceCommand.kt | 3 +- src/main/resources/META-INF/plugin.xml | 4 + 20 files changed, 397 insertions(+), 68 deletions(-) create mode 100644 src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.form create mode 100644 src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java rename src/main/java/com/developerphil/adbidea/ui/{InteractingForm.form => InteractingFrame.form} (99%) rename src/main/java/com/developerphil/adbidea/ui/{InteractingForm.java => InteractingFrame.java} (97%) create mode 100644 src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/ActivityServiceCommand.kt (94%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/CommonStringResultCommand.kt (56%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/ForceStopCommand.kt (94%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/ForegroundActivityCommand.kt (93%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/GetApplicationListCommand.kt (91%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/InstallApkCommand.kt (90%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/InteractingCommand.kt (95%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/PackageDetailCommand.kt (94%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/PackagePathCommand.kt (94%) rename src/main/kotlin/com/developerphil/adbidea/adb/{ => command}/PutStringToDeviceCommand.kt (91%) diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index 59b1ec20..a8a1dcbd 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -2,12 +2,22 @@ import com.android.ddmlib.IDevice; import com.developerphil.adbidea.ObjectGraph; +import com.developerphil.adbidea.adb.command.ActivityServiceCommand; 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.PackageDetailCommand; +import com.developerphil.adbidea.adb.command.PackagePathCommand; +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; @@ -138,4 +148,8 @@ public static void putStringToDevice(@Nullable Project project, @NotNull String 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)); + } } diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java index c13f1baf..01692e85 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java @@ -7,7 +7,6 @@ import com.intellij.uiDesigner.core.GridLayoutManager; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; -import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -34,11 +33,6 @@ import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.event.MouseInputAdapter; -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; import org.jetbrains.annotations.Nullable; /** @@ -171,9 +165,9 @@ public void mouseClicked(MouseEvent e) { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - append2Ta("View " + name + " detail : \n", JBColor.BLUE); + Utils.append2TextPane("View " + name + " detail : \n", JBColor.BLUE, tp); AdbFacade.getPackageDetail(mProject, name, s -> { - append2Ta(s); + Utils.append2TextPane(s, tp); return null; }); } @@ -182,9 +176,9 @@ public void mouseClicked(MouseEvent e) { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - append2Ta("View " + name + "apk path : \n", JBColor.BLUE); + Utils.append2TextPane("View " + name + "apk path : \n", JBColor.BLUE, tp); AdbFacade.getPackagePath(mProject, name, s -> { - append2Ta(s); + Utils.append2TextPane(s, tp); return null; }); } @@ -194,24 +188,24 @@ public void mouseClicked(MouseEvent e) { if (selectedValuesList.isEmpty()) { String keywordText = tv_keyword.getText(); if (!Utils.isEmpty(keywordText)) { - append2Ta("View running services related with" + keywordText + " : \n", JBColor.BLUE); + Utils.append2TextPane("View running services related with" + keywordText + " : \n", JBColor.BLUE, tp); AdbFacade.getActivityService(mProject, keywordText, s -> { - append2Ta(s); + Utils.append2TextPane(s, tp); return null; }); } else { - append2Ta("View all running services : \n", JBColor.BLUE); + Utils.append2TextPane("View all running services : \n", JBColor.BLUE, tp); AdbFacade.getActivityService(mProject, "", s -> { - append2Ta(s); + Utils.append2TextPane(s, tp); return null; }); } } for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - append2Ta("View running services related with" + name + " : \n", JBColor.BLUE); + Utils.append2TextPane("View running services related with" + name + " : \n", JBColor.BLUE, tp); AdbFacade.getActivityService(mProject, name, s -> { - append2Ta(s); + Utils.append2TextPane(s, tp); return null; }); } @@ -220,14 +214,14 @@ public void mouseClicked(MouseEvent e) { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - append2Ta("Force-stop : " + name + "\n", JBColor.BLUE); + Utils.append2TextPane("Force-stop : " + name + "\n", JBColor.BLUE, tp); AdbFacade.forceStop(mProject, name); } }); mForegroundActivityButton.addActionListener(e -> { - append2Ta("Foreground Activity : \n", JBColor.BLUE); + Utils.append2TextPane("Foreground Activity : \n", JBColor.BLUE, tp); AdbFacade.showForegroundActivity(mProject, s -> { - append2Ta(s); + Utils.append2TextPane(s, tp); return null; }); }); @@ -235,32 +229,13 @@ public void mouseClicked(MouseEvent e) { // List selectedValuesList = mJList.getSelectedValuesList(); // for (String packageName : selectedValuesList) { // String name = getRealPackageName(packageName); - // append2Ta("Trim Memory of " + name + ":\n", JBColor.BLUE); + // Utils.append2TextPane("Trim Memory of " + name + ":\n", JBColor.BLUE); // AdbFacade.trimMemory(mProject, name); // } //}); setContentPane($$$getRootComponent$$$()); } - private void append2Ta(String str, Color color) { - Document doc = tp.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) { - } - } - } - - private void append2Ta(String str) { - append2Ta(str, null); - } private String getRealPackageName(String packageName) { if (mShowApkFileCheckBox.isSelected() && mShowInstallersCheckBox.isSelected()) { diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.form b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.form new file mode 100644 index 00000000..8c9822f9 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.form @@ -0,0 +1,130 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java new file mode 100644 index 00000000..a9083033 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java @@ -0,0 +1,153 @@ +package com.developerphil.adbidea.ui; + +import com.developerphil.adbidea.adb.AdbFacade; +import com.intellij.openapi.project.Project; +import com.intellij.ui.JBColor; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.MouseEvent; +import java.net.URL; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JTextPane; +import javax.swing.event.MouseInputAdapter; +import org.jetbrains.annotations.Nullable; + +/** + * Created by XQ Yang on 10/11/2018 11:45 AM. + * Description : + */ + +public class DeviceInfoFrame extends JFrame { + private final Project mProject; + private final JPopupMenu mPopupMenu; + private JPanel mPanel; + private JTextPane mTextPane; + private JButton mDisplaysInfoButton; + private JButton mCPUInfoButton; + private JButton mMemoryInfoButton; + private JButton mScreenInfoButton; + private JButton mBatteryInfoButton; + private JButton mSystemButton; + private JButton mNetworkButton; + private JButton mMoreButton; + + private String androidVersion = ""; + + public DeviceInfoFrame(@Nullable Project project) { + setResizable(true); + Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); + int x = (int) screensize.getWidth() / 2 - mPanel.getPreferredSize().width / 2; + int y = (int) screensize.getHeight() / 2 - mPanel.getPreferredSize().height / 2; + setTitle("Adb Interacting with applications"); + URL filename = getClass().getResource("/icon.png"); + ImageIcon icon = new ImageIcon(filename); + setIconImage(icon.getImage()); + setLocation(x, y); + mProject = project; + mPopupMenu = new JPopupMenu(); + JMenuItem clear = mPopupMenu.add(new JMenuItem("clear")); + clear.addActionListener(e -> mTextPane.setText("")); + mTextPane.addMouseListener(new MouseInputAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + mPopupMenu.show(mTextPane, e.getX(), e.getY()); + } + } + }); + //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); + return null; + }); + AdbFacade.getSimpleInfo(mProject, "getprop ro.product.model", "get Device model ", s1 -> { + Utils.append2TextPaneNewLine("Device model:", JBColor.BLUE, mTextPane); + Utils.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); + return null; + }); + AdbFacade.getSimpleInfo(mProject, "getprop ro.build.version.release", "get Android Version ", s1 -> { + androidVersion = s1.trim(); + return null; + }); + mDisplaysInfoButton.addActionListener(e -> getInfo2Show("Displays Info:", "dumpsys window displays", "get Displays Info ")); + mCPUInfoButton.addActionListener(e -> { + getInfo2Show("CPU Info:", "cat /proc/cpuinfo", "get CPU Info "); + getInfo2Show("Support ABI list:", "getprop ro.product.cpu.abilist", "Support ABI list "); + }); + mMemoryInfoButton.addActionListener(e -> { + getInfo2Show("Memory Info:", "cat /proc/meminfo", "get Memory Info "); + getInfo2Show("Support heap size:", "getprop dalvik.vm.heapsize", "get Support heap "); + }); + mScreenInfoButton.addActionListener(e -> { + getInfo2Show("Screen Size:", "wm size", "get Screen Size "); + getInfo2Show("Screen density:", "wm density", "get Screen density "); + }); + mBatteryInfoButton.addActionListener(e -> { + getInfo2Show("Battery Service state:", "dumpsys battery", "get Battery Service state "); + }); + mMoreButton.addActionListener(e -> { + getInfo2Show("More system and hardware information:", "cat /system/build.prop", "get System and hardware information "); + }); + 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); + return null; + }); + if (!Utils.isEmpty(androidVersion)) { + Utils.append2TextPaneNewLine("Android Version:", JBColor.BLUE, mTextPane); + Utils.append2TextPaneNewLine(androidVersion, mTextPane); + } + + }); + mNetworkButton.addActionListener(e -> { + getInfo2Show("Android id:", "settings get secure android_id", "get Android id "); + if (!Utils.isEmpty(androidVersion)) { + try { + int verison = Integer.parseInt(androidVersion.substring(0, 1)); + if (verison >= 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 "); + } else { + getInfo2Show("IMEI 1 :", "dumpsys iphonesubinfo", "get IMEI 1 "); + } + } catch (NumberFormatException e1) { + e1.printStackTrace(); + } + } + AdbFacade.getSimpleInfo(mProject, "ifconfig | grep Mask", "get IP Address ", s -> { + Utils.append2TextPaneNewLine("ifconfig :", JBColor.BLUE, mTextPane); + Utils.append2TextPaneNewLine(s, mTextPane); + return null; + }); + AdbFacade.getSimpleInfo(mProject, "ifconfig wlan0", "get IP Address ", s1 -> { + Utils.append2TextPaneNewLine("wlan0 :", JBColor.BLUE, mTextPane); + Utils.append2TextPaneNewLine(s1, mTextPane); + return null; + }); + getInfo2Show("Mac Address:", "cat /sys/class/net/wlan0/address", "get Mac Address "); + }); + + setContentPane(mPanel); + } + + 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); + return null; + }); + } +} diff --git a/src/main/java/com/developerphil/adbidea/ui/InteractingForm.form b/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.form similarity index 99% rename from src/main/java/com/developerphil/adbidea/ui/InteractingForm.form rename to src/main/java/com/developerphil/adbidea/ui/InteractingFrame.form index 9e007f95..f1cac1e8 100644 --- a/src/main/java/com/developerphil/adbidea/ui/InteractingForm.form +++ b/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.form @@ -1,5 +1,5 @@ -
    + diff --git a/src/main/java/com/developerphil/adbidea/ui/InteractingForm.java b/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.java similarity index 97% rename from src/main/java/com/developerphil/adbidea/ui/InteractingForm.java rename to src/main/java/com/developerphil/adbidea/ui/InteractingFrame.java index accc095b..c09db12b 100644 --- a/src/main/java/com/developerphil/adbidea/ui/InteractingForm.java +++ b/src/main/java/com/developerphil/adbidea/ui/InteractingFrame.java @@ -26,7 +26,7 @@ * Description : */ -public class InteractingForm extends JFrame { +public class InteractingFrame extends JFrame { private final Project mProject; private JPanel mPanel; @@ -43,7 +43,7 @@ public class InteractingForm extends JFrame { private JButton mClearButton; private BoundTableModel mModel; - public InteractingForm(@Nullable Project project) { + public InteractingFrame(@Nullable Project project) { setResizable(true); Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); int x = (int) screensize.getWidth() / 2 - mPanel.getPreferredSize().width / 2; diff --git a/src/main/java/com/developerphil/adbidea/ui/Utils.java b/src/main/java/com/developerphil/adbidea/ui/Utils.java index 7ffea696..222873d1 100644 --- a/src/main/java/com/developerphil/adbidea/ui/Utils.java +++ b/src/main/java/com/developerphil/adbidea/ui/Utils.java @@ -1,5 +1,13 @@ 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 : @@ -12,4 +20,32 @@ 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/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt index 0bba65e4..f25427e4 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/InteractingAction.kt @@ -1,7 +1,7 @@ package com.developerphil.adbidea.action.extend import com.developerphil.adbidea.action.AdbAction -import com.developerphil.adbidea.ui.InteractingForm +import com.developerphil.adbidea.ui.InteractingFrame import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.Project @@ -14,7 +14,7 @@ class InteractingAction : AdbAction() { override fun actionPerformed(e: AnActionEvent?, project: Project?) { - val form = InteractingForm(project) + val form = InteractingFrame(project) form.pack() form.isVisible = true } diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt new file mode 100644 index 00000000..832d6a90 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ShowDeviceInfoAction.kt @@ -0,0 +1,22 @@ +package com.developerphil.adbidea.action.extend + +import com.developerphil.adbidea.action.AdbAction +import com.developerphil.adbidea.ui.DeviceInfoFrame +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.Project + + +/** + * Created by XQ Yang on 8/28/2018 2:53 PM. + * Description : input simple string to device + */ +class ShowDeviceInfoAction : AdbAction() { + + + 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/ActivityServiceCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt similarity index 94% rename from src/main/kotlin/com/developerphil/adbidea/adb/ActivityServiceCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt index 6b6084ab..ad44c44e 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/ActivityServiceCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt @@ -1,8 +1,7 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.* diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt similarity index 56% rename from src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt index 0606810d..6cdfb07b 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/CommonStringResultCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt @@ -1,7 +1,6 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.* @@ -10,16 +9,20 @@ import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet import java.util.concurrent.TimeUnit -class CommonStringResultCommand(val commandStr:String,val operationDesc:String) : Command { +class CommonStringResultCommand(private val commandStr:String,private val operationDesc:String,private val callback:((String)->Unit)? = null) : Command { override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { try { val receiver = PrintReceiver() device.executeShellCommand(commandStr, receiver, 15L, TimeUnit.SECONDS) - 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) + if (callback==null) { + 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) + } else { + callback.invoke(receiver.toString()) + } return true } catch (e1: Exception) { error("$operationDesc... " + e1.message) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/ForceStopCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForceStopCommand.kt similarity index 94% rename from src/main/kotlin/com/developerphil/adbidea/adb/ForceStopCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/ForceStopCommand.kt index 9e2fadb2..31305dcc 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/ForceStopCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForceStopCommand.kt @@ -1,8 +1,7 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.* diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/ForegroundActivityCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt similarity index 93% rename from src/main/kotlin/com/developerphil/adbidea/adb/ForegroundActivityCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt index f1c5d76b..608ae5dd 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/ForegroundActivityCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt @@ -1,7 +1,6 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.* diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/GetApplicationListCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/GetApplicationListCommand.kt similarity index 91% rename from src/main/kotlin/com/developerphil/adbidea/adb/GetApplicationListCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/GetApplicationListCommand.kt index defe35a9..d888a177 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/GetApplicationListCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/GetApplicationListCommand.kt @@ -1,7 +1,6 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.GenericReceiver import com.developerphil.adbidea.ui.NotificationHelper.error import com.intellij.openapi.project.Project diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/InstallApkCommand.kt similarity index 90% rename from src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/InstallApkCommand.kt index 121167f0..45c605ef 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/InstallApkCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/InstallApkCommand.kt @@ -1,8 +1,7 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.android.ddmlib.InstallException -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.ui.NotificationHelper.error import com.developerphil.adbidea.ui.NotificationHelper.info import com.intellij.openapi.project.Project diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/InteractingCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/InteractingCommand.kt similarity index 95% rename from src/main/kotlin/com/developerphil/adbidea/adb/InteractingCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/InteractingCommand.kt index a90ed59d..3fa17608 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/InteractingCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/InteractingCommand.kt @@ -1,4 +1,4 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.developerphil.adbidea.bean.BoundItemBean diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/PackageDetailCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt similarity index 94% rename from src/main/kotlin/com/developerphil/adbidea/adb/PackageDetailCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt index dce91aa2..03ceaa9b 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/PackageDetailCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt @@ -1,8 +1,7 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.* diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/PackagePathCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt similarity index 94% rename from src/main/kotlin/com/developerphil/adbidea/adb/PackagePathCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt index 81b58458..4d55cbdc 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/PackagePathCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt @@ -1,8 +1,7 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.adb.AdbUtil.isAppInstalled -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.* diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/PutStringToDeviceCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/PutStringToDeviceCommand.kt similarity index 91% rename from src/main/kotlin/com/developerphil/adbidea/adb/PutStringToDeviceCommand.kt rename to src/main/kotlin/com/developerphil/adbidea/adb/command/PutStringToDeviceCommand.kt index b23db8ba..14d0b02f 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/PutStringToDeviceCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PutStringToDeviceCommand.kt @@ -1,7 +1,6 @@ -package com.developerphil.adbidea.adb +package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice -import com.developerphil.adbidea.adb.command.Command import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.intellij.openapi.project.Project diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 148203bb..c1427671 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -203,6 +203,10 @@ class="com.developerphil.adbidea.action.extend.InteractingAction" text="ADB Application Interacting Popup..." description="Interacting with applications, e.g. start activity ,send broadcast"> + + From 31bd91a69325d71ac49abcc618e5076c082e929f Mon Sep 17 00:00:00 2001 From: longforus Date: Thu, 11 Oct 2018 18:23:32 +0800 Subject: [PATCH 09/43] add captureScreen but not success --- .../developerphil/adbidea/adb/AdbFacade.java | 5 ++ .../action/extend/ScreenCaptureAction.kt | 47 +++++++++++++++++ .../adb/command/CaptureScreenCommand.kt | 50 +++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 4 ++ 4 files changed, 106 insertions(+) create mode 100644 src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index a8a1dcbd..dd4511d1 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -3,6 +3,7 @@ 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; @@ -152,4 +153,8 @@ public static void interacting(Project project, int type, String action, String 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, @Nullable File dir,String name) { + executeOnDevice(project,new CaptureScreenCommand(dir,name)); + } } diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt new file mode 100644 index 00000000..6ddde58c --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt @@ -0,0 +1,47 @@ +package com.developerphil.adbidea.action.extend + +import com.developerphil.adbidea.action.AdbAction +import com.developerphil.adbidea.adb.AdbFacade +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.fileChooser.FileChooserDescriptor +import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + + +/** + * Created by XQ Yang on 8/28/2018 2:53 PM. + * Description : capture device screen via adb + */ +class ScreenCaptureAction : AdbAction() { + + init { + saveDirChooserDescriptor.title = "Select capture png file save to..." + } + + + override fun actionPerformed(e: AnActionEvent?, project: Project?) { + if (deviceName.isEmpty()) { + AdbFacade.getSimpleInfo(project, "getprop ro.product.model", "get Device model ") { name -> + deviceName = name.replace("\r\n","").replace(" ","") + } + } + val choose = FileChooserDialogImpl(saveDirChooserDescriptor, project) + .choose(project, selectedFile) + if (choose.isNotEmpty()) { + selectedFile = choose[0] + AdbFacade.captureScreen(project, File(selectedFile?.canonicalPath),"${deviceName}_${dateFormat.format(Date())}.png") + } + } + + companion object { + private var selectedFile: VirtualFile? = null + private var saveDirChooserDescriptor: FileChooserDescriptor = FileChooserDescriptor(false, true, false, false, false, false) + private var dateFormat = SimpleDateFormat("yyyyMMddHHmmss") + var deviceName = "" + } + +} diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt new file mode 100644 index 00000000..fc02105d --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt @@ -0,0 +1,50 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.developerphil.adbidea.ui.NotificationHelper +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.io.BufferedReader +import java.io.File +import java.io.InputStreamReader + +/** + * Created by XQ Yang on 10/10/2018 10:56 AM. + * Description : + */ +class CaptureScreenCommand(val dir: File, val name: String) : Command { + override fun run(project: Project?, device: IDevice?, facet: AndroidFacet?, packageName: String?): Boolean { + try { + val runtime = Runtime.getRuntime() + val p = runtime.exec("adb exec-out screencap -p > "+name,null,dir) + val fis = p.getInputStream() + var isr = InputStreamReader(fis) + var br = BufferedReader(isr) + var line = br.readLine() + val sb = StringBuilder() + while (line != null) { + sb.append(line) + line = br.readLine() + } + if (sb.length > 0) { + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", sb.toString(), NotificationType.INFORMATION, NotificationHelper.NOOP_LISTENER) + notification.notify(project) + } + br.close() + isr.close() + fis.close() +// device?.executeShellCommand("exec-out screencap -p > $path", receiver, 15L, TimeUnit.SECONDS) +// if (receiver.toString().isNotEmpty()) { +// val string = receiver.toString() +// val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NotificationHelper.NOOP_LISTENER) +// notification.notify(project) +// } + return true + } catch (e1: Exception) { + NotificationHelper.error("Put String to device... " + e1.message) + } + return false + } + +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index c1427671..a85e9ee7 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -215,6 +215,10 @@ class="com.developerphil.adbidea.action.extend.PutStringAction" text="ADB Put simple string to device" description="Input simple string to device"> + + From 09d037cbff3cffc7304592198b4868c8871e292d Mon Sep 17 00:00:00 2001 From: longforus Date: Fri, 12 Oct 2018 14:32:41 +0800 Subject: [PATCH 10/43] achieve screen capture --- .../action/extend/ScreenCaptureAction.kt | 2 +- .../adb/command/CaptureScreenCommand.kt | 21 +++++++------------ .../com/developerphil/adbidea/TempTest.kt | 12 +++++++++++ 3 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 src/test/kotlin/com/developerphil/adbidea/TempTest.kt 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 6ddde58c..7bb859e0 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt @@ -26,7 +26,7 @@ class ScreenCaptureAction : AdbAction() { override fun actionPerformed(e: AnActionEvent?, project: Project?) { if (deviceName.isEmpty()) { AdbFacade.getSimpleInfo(project, "getprop ro.product.model", "get Device model ") { name -> - deviceName = name.replace("\r\n","").replace(" ","") + deviceName = name.replace("\n","").replace("\r","").replace(" ","") } } val choose = FileChooserDialogImpl(saveDirChooserDescriptor, project) 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 fc02105d..72d8fb08 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt @@ -4,6 +4,7 @@ import com.android.ddmlib.IDevice import com.developerphil.adbidea.ui.NotificationHelper import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project +import org.jdesktop.swingx.util.OS import org.jetbrains.android.facet.AndroidFacet import java.io.BufferedReader import java.io.File @@ -17,10 +18,12 @@ class CaptureScreenCommand(val dir: File, val name: String) : Command { override fun run(project: Project?, device: IDevice?, facet: AndroidFacet?, packageName: String?): Boolean { try { val runtime = Runtime.getRuntime() - val p = runtime.exec("adb exec-out screencap -p > "+name,null,dir) - val fis = p.getInputStream() - var isr = InputStreamReader(fis) - var br = BufferedReader(isr) + val p: Process = if (OS.isWindows()) { + runtime.exec(arrayOf("cmd", "/C", "adb exec-out screencap -p > " + File(dir, name).absolutePath)) + } else { + runtime.exec(arrayOf("/bin/sh","-c","adb exec-out screencap -p > "+File(dir,name).absolutePath)) + } + val br = BufferedReader(InputStreamReader(p.inputStream)) var line = br.readLine() val sb = StringBuilder() while (line != null) { @@ -32,17 +35,9 @@ class CaptureScreenCommand(val dir: File, val name: String) : Command { notification.notify(project) } br.close() - isr.close() - fis.close() -// device?.executeShellCommand("exec-out screencap -p > $path", receiver, 15L, TimeUnit.SECONDS) -// if (receiver.toString().isNotEmpty()) { -// val string = receiver.toString() -// val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NotificationHelper.NOOP_LISTENER) -// notification.notify(project) -// } return true } catch (e1: Exception) { - NotificationHelper.error("Put String to device... " + e1.message) + NotificationHelper.error("Capture Screen ... " + e1.message) } return false } diff --git a/src/test/kotlin/com/developerphil/adbidea/TempTest.kt b/src/test/kotlin/com/developerphil/adbidea/TempTest.kt new file mode 100644 index 00000000..3721a6ff --- /dev/null +++ b/src/test/kotlin/com/developerphil/adbidea/TempTest.kt @@ -0,0 +1,12 @@ +package com.developerphil.adbidea + +import javax.swing.JOptionPane + +/** + * Created by XQ Yang on 10/10/2018 10:46 AM. + * Description : + */ +fun main(args: Array) { + val reslut = JOptionPane.showInputDialog("Input simple string to device") + println(reslut) +} \ No newline at end of file From b9f24662ea84c2791ced8fdbd73ad6f07a512896 Mon Sep 17 00:00:00 2001 From: longforus Date: Fri, 12 Oct 2018 18:00:20 +0800 Subject: [PATCH 11/43] add screen record --- .../developerphil/adbidea/adb/AdbFacade.java | 9 +- .../adbidea/ui/RecordOptionDialog.form | 104 ++++++++++++++++++ .../adbidea/ui/RecordOptionDialog.java | 81 ++++++++++++++ .../action/extend/ScreenCaptureAction.kt | 8 +- .../action/extend/ScreenRecordAction.kt | 53 +++++++++ .../adb/command/CaptureScreenCommand.kt | 11 +- .../adb/command/ScreenRecordCommand.kt | 56 ++++++++++ src/main/resources/META-INF/plugin.xml | 4 + 8 files changed, 315 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form create mode 100644 src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java create mode 100644 src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index dd4511d1..ddd28d59 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -22,6 +22,7 @@ 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; @@ -154,7 +155,11 @@ public static void getSimpleInfo(Project project, String command, String desc,Fu executeOnDevice(project,new CommonStringResultCommand(command, desc,callback)); } - public static void captureScreen(@Nullable Project project, @Nullable File dir,String name) { - executeOnDevice(project,new CaptureScreenCommand(dir,name)); + public static void captureScreen(@Nullable Project project, String path) { + executeOnDevice(project,new CaptureScreenCommand(path)); + } + public static void recordScreen(@Nullable Project project, String path,String videoName,int length,boolean showTouches) { + ScreenRecordCommand command = new ScreenRecordCommand(path, videoName,length,showTouches); + executeOnDevice(project, command); } } diff --git a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form new file mode 100644 index 00000000..35bb8392 --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java new file mode 100644 index 00000000..3943678d --- /dev/null +++ b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java @@ -0,0 +1,81 @@ +package com.developerphil.adbidea.ui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.KeyStroke; +import kotlin.Unit; +import kotlin.jvm.functions.Function2; +import org.jetbrains.annotations.NotNull; + +public class RecordOptionDialog extends JDialog { + private JPanel contentPane; + private JButton buttonOK; + private JButton buttonCancel; + private JCheckBox mShowCheckBox; + private JSlider mSliderTime; + @NotNull + public Function2 okListener; + + public RecordOptionDialog(@NotNull Function2 okListener) { + setContentPane(contentPane); + setModal(true); + getRootPane().setDefaultButton(buttonOK); + this.okListener = okListener; + + + mSliderTime.setMajorTickSpacing(10); + + mSliderTime.setMinorTickSpacing(2); + + mSliderTime.setPaintTicks(true); + mSliderTime.setPaintLabels(true); + + + buttonOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onOK(); + } + }); + + buttonCancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }); + + // call onCancel() when cross is clicked + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + onCancel(); + } + }); + + // call onCancel() on ESCAPE + contentPane.registerKeyboardAction(new ActionListener() { + public void actionPerformed(ActionEvent e) { + onCancel(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + } + + private void onOK() { + okListener.invoke(mShowCheckBox.isSelected(), mSliderTime.getValue()); + dispose(); + } + + private void onCancel() { + // add your code here if necessary + dispose(); + } + +} 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 7bb859e0..9ae6f09d 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt @@ -17,7 +17,7 @@ import java.util.* * Description : capture device screen via adb */ class ScreenCaptureAction : AdbAction() { - + var deviceName = "" init { saveDirChooserDescriptor.title = "Select capture png file save to..." } @@ -26,14 +26,14 @@ class ScreenCaptureAction : AdbAction() { override fun actionPerformed(e: AnActionEvent?, project: Project?) { if (deviceName.isEmpty()) { AdbFacade.getSimpleInfo(project, "getprop ro.product.model", "get Device model ") { name -> - deviceName = name.replace("\n","").replace("\r","").replace(" ","") + deviceName = name.replace("\n", "").replace("\r", "").replace(" ", "") } } val choose = FileChooserDialogImpl(saveDirChooserDescriptor, project) .choose(project, selectedFile) if (choose.isNotEmpty()) { selectedFile = choose[0] - AdbFacade.captureScreen(project, File(selectedFile?.canonicalPath),"${deviceName}_${dateFormat.format(Date())}.png") + AdbFacade.captureScreen(project, File(selectedFile?.canonicalPath, "${deviceName}_${dateFormat.format(Date())}.png").absolutePath) } } @@ -41,7 +41,7 @@ class ScreenCaptureAction : AdbAction() { private var selectedFile: VirtualFile? = null private var saveDirChooserDescriptor: FileChooserDescriptor = FileChooserDescriptor(false, true, false, false, false, false) private var dateFormat = SimpleDateFormat("yyyyMMddHHmmss") - var deviceName = "" + } } diff --git a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt new file mode 100644 index 00000000..8f53ccaf --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt @@ -0,0 +1,53 @@ +package com.developerphil.adbidea.action.extend + +import com.developerphil.adbidea.action.AdbAction +import com.developerphil.adbidea.adb.AdbFacade +import com.developerphil.adbidea.ui.RecordOptionDialog +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.fileChooser.FileChooserDescriptor +import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + + +/** + * Created by XQ Yang on 8/28/2018 2:53 PM. + * Description : record device screen via adb + */ +class ScreenRecordAction : AdbAction() { + var deviceName = "" + init { + saveDirChooserDescriptor.title = "Select record .mp4 file save to..." + } + + + override fun actionPerformed(e: AnActionEvent?, project: Project?) { + if (deviceName.isEmpty()) { + AdbFacade.getSimpleInfo(project, "getprop ro.product.model", "get Device model ") { name -> + deviceName = name.replace("\n", "").replace("\r", "").replace(" ", "") + } + } + val choose = FileChooserDialogImpl(saveDirChooserDescriptor, project) + .choose(project, selectedFile) + if (choose.isNotEmpty()) { + val dialog = RecordOptionDialog { showTouches,length-> + selectedFile = choose[0] + val videoName = "${deviceName}_${dateFormat.format(Date())}.mp4" + AdbFacade.recordScreen(project, File(selectedFile?.canonicalPath, videoName).absolutePath,videoName,length,showTouches) + } + dialog.pack() + dialog.isVisible = true + } + } + + companion object { + private var selectedFile: VirtualFile? = null + private var saveDirChooserDescriptor: FileChooserDescriptor = FileChooserDescriptor(false, true, false, false, false, false) + private var dateFormat = SimpleDateFormat("yyyyMMddHHmmss") + + } + +} 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 72d8fb08..2a583299 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt @@ -2,26 +2,26 @@ package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.developerphil.adbidea.ui.NotificationHelper +import com.intellij.ide.actions.OpenFileAction import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jdesktop.swingx.util.OS import org.jetbrains.android.facet.AndroidFacet import java.io.BufferedReader -import java.io.File import java.io.InputStreamReader /** * Created by XQ Yang on 10/10/2018 10:56 AM. * Description : */ -class CaptureScreenCommand(val dir: File, val name: String) : Command { - override fun run(project: Project?, device: IDevice?, facet: AndroidFacet?, packageName: String?): Boolean { +class CaptureScreenCommand(val path: String) : Command { + override fun run(project: Project, device: IDevice?, facet: AndroidFacet?, packageName: String?): Boolean { try { val runtime = Runtime.getRuntime() val p: Process = if (OS.isWindows()) { - runtime.exec(arrayOf("cmd", "/C", "adb exec-out screencap -p > " + File(dir, name).absolutePath)) + runtime.exec(arrayOf("cmd", "/C", "adb exec-out screencap -p > $path")) } else { - runtime.exec(arrayOf("/bin/sh","-c","adb exec-out screencap -p > "+File(dir,name).absolutePath)) + runtime.exec(arrayOf("/bin/sh","-c","adb exec-out screencap -p > $path")) } val br = BufferedReader(InputStreamReader(p.inputStream)) var line = br.readLine() @@ -35,6 +35,7 @@ class CaptureScreenCommand(val dir: File, val name: String) : Command { notification.notify(project) } br.close() + OpenFileAction.openFile(path, project) return true } catch (e1: Exception) { NotificationHelper.error("Capture Screen ... " + e1.message) diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt new file mode 100644 index 00000000..61ca7123 --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt @@ -0,0 +1,56 @@ +package com.developerphil.adbidea.adb.command + +import com.android.ddmlib.IDevice +import com.android.ddmlib.ScreenRecorderOptions +import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.ui.NotificationHelper +import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER +import com.developerphil.adbidea.ui.NotificationHelper.error +import com.intellij.ide.actions.OpenFileAction +import com.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.* +import java.util.concurrent.TimeUnit + +/** + * Created by XQ Yang on 2018-10-9 15:00:43 + * Description : + */ + +class ScreenRecordCommand(private val savePath: String, videoName: String, val length: Int, val showTouches: Boolean = false) : Command { + + lateinit var mDevice: IDevice + val receiver = PrintReceiver() + private val remotePath = "/sdcard/$videoName" + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + mDevice = device + try { + val options = ScreenRecorderOptions.Builder().setTimeLimit(length.toLong(), TimeUnit.SECONDS).setShowTouches(showTouches).build() + device.startScreenRecorder(remotePath, options, receiver) + val timer = Timer() + timer.schedule(object : TimerTask() { + override fun run() { + device.pullFile(remotePath, savePath) + OpenFileAction.openFile(savePath, project) + //todo:need optimize + timer.schedule(object : TimerTask() { + override fun run() { + device.executeShellCommand("rm $remotePath",receiver,10L,TimeUnit.SECONDS) + } + }, 5 * 1000L) + } + }, (length + 1) * 1000L) + val string = receiver.toString() + val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) + notification.notify(project) + return true + } catch (e1: Exception) { + error("Record Screen... " + e1.message) + } + return false + } + + +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index a85e9ee7..dc29b0b3 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -215,6 +215,10 @@ class="com.developerphil.adbidea.action.extend.PutStringAction" text="ADB Put simple string to device" description="Input simple string to device">
    + + From 337e0c8df6bf7d2edaa777d287599d84481abb09 Mon Sep 17 00:00:00 2001 From: longforus Date: Sat, 13 Oct 2018 14:43:30 +0800 Subject: [PATCH 12/43] finish screen record --- .../developerphil/adbidea/adb/AdbFacade.java | 14 ++++---- .../developerphil/adbidea/adb/AdbUtil.java | 34 ++++++++++++++++-- .../adbidea/ui/DeviceInfoFrame.java | 2 +- .../adbidea/ui/RecordOptionDialog.form | 15 +++++++- .../adbidea/ui/RecordOptionDialog.java | 22 ++++++++++-- .../action/extend/ScreenRecordAction.kt | 2 +- .../adb/command/ScreenRecordCommand.kt | 35 ++++++++++++++----- src/main/resources/META-INF/plugin.xml | 2 +- 8 files changed, 102 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index ddd28d59..5d45cd4a 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -148,18 +148,18 @@ public static void putStringToDevice(@Nullable Project project, @NotNull String } 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)); + 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 getSimpleInfo(Project project, String command, String desc, Function1 callback) { + executeOnDevice(project, new CommonStringResultCommand(command, desc, callback)); } public static void captureScreen(@Nullable Project project, String path) { - executeOnDevice(project,new CaptureScreenCommand(path)); + executeOnDevice(project, new CaptureScreenCommand(path)); } - public static void recordScreen(@Nullable Project project, String path,String videoName,int length,boolean showTouches) { - ScreenRecordCommand command = new ScreenRecordCommand(path, videoName,length,showTouches); - executeOnDevice(project, command); + + public static void recordScreen(@Nullable Project project, File localFile, String videoName, int length, boolean showTouches) { + executeOnDevice(project, new ScreenRecordCommand(localFile, videoName, length, showTouches)); } } diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbUtil.java b/src/main/java/com/developerphil/adbidea/adb/AdbUtil.java index be37c8d1..77878ba2 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbUtil.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbUtil.java @@ -2,16 +2,19 @@ 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 org.joor.Reflect; - +import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; +import org.joor.Reflect; public class AdbUtil { @@ -45,8 +48,35 @@ public static boolean isGradleSyncInProgress(Project project) { 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/ui/DeviceInfoFrame.java b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java index a9083033..90e18410 100644 --- a/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java @@ -44,10 +44,10 @@ public DeviceInfoFrame(@Nullable Project project) { int x = (int) screensize.getWidth() / 2 - mPanel.getPreferredSize().width / 2; int y = (int) screensize.getHeight() / 2 - mPanel.getPreferredSize().height / 2; setTitle("Adb Interacting with applications"); + setLocation(x, y); URL filename = getClass().getResource("/icon.png"); ImageIcon icon = new ImageIcon(filename); setIconImage(icon.getImage()); - setLocation(x, y); mProject = project; mPopupMenu = new JPopupMenu(); JMenuItem clear = mPopupMenu.add(new JMenuItem("clear")); diff --git a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form index 35bb8392..2108174a 100644 --- a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form +++ b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.form @@ -5,7 +5,9 @@ - + + + @@ -53,6 +55,8 @@ + + @@ -97,6 +101,15 @@ + + + + + + + + + diff --git a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java index 3943678d..ed8e369b 100644 --- a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java @@ -1,5 +1,7 @@ package com.developerphil.adbidea.ui; +import java.awt.Dimension; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -9,9 +11,12 @@ import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.KeyStroke; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import kotlin.Unit; import kotlin.jvm.functions.Function2; import org.jetbrains.annotations.NotNull; @@ -22,6 +27,7 @@ public class RecordOptionDialog extends JDialog { private JButton buttonCancel; private JCheckBox mShowCheckBox; private JSlider mSliderTime; + private JLabel mJLabel; @NotNull public Function2 okListener; @@ -30,7 +36,11 @@ public RecordOptionDialog(@NotNull Function2 okListener) { setModal(true); getRootPane().setDefaultButton(buttonOK); this.okListener = okListener; - + Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); + int x = (int) screensize.getWidth() / 2 - contentPane.getPreferredSize().width / 2; + int y = (int) screensize.getHeight() / 2 - contentPane.getPreferredSize().height / 2; + setTitle("Adb set record option"); + setLocation(x, y); mSliderTime.setMajorTickSpacing(10); @@ -39,7 +49,13 @@ public RecordOptionDialog(@NotNull Function2 okListener) { mSliderTime.setPaintTicks(true); mSliderTime.setPaintLabels(true); - + mJLabel.setText("Selected length "+mSliderTime.getValue()+"s"); + mSliderTime.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + mJLabel.setText("Selected length "+mSliderTime.getValue()+"s"); + } + }); buttonOK.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { onOK(); @@ -69,7 +85,7 @@ public void actionPerformed(ActionEvent e) { } private void onOK() { - okListener.invoke(mShowCheckBox.isSelected(), mSliderTime.getValue()); + okListener.invoke(mShowCheckBox.isSelected(), mSliderTime.getValue()+1); dispose(); } 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 8f53ccaf..083034d8 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt @@ -36,7 +36,7 @@ class ScreenRecordAction : AdbAction() { val dialog = RecordOptionDialog { showTouches,length-> selectedFile = choose[0] val videoName = "${deviceName}_${dateFormat.format(Date())}.mp4" - AdbFacade.recordScreen(project, File(selectedFile?.canonicalPath, videoName).absolutePath,videoName,length,showTouches) + AdbFacade.recordScreen(project, File(selectedFile?.canonicalPath, videoName),videoName,length,showTouches) } dialog.pack() dialog.isVisible = true diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt index 61ca7123..1159ff19 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt @@ -2,14 +2,17 @@ package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice import com.android.ddmlib.ScreenRecorderOptions +import com.android.ddmlib.SyncService +import com.developerphil.adbidea.adb.AdbUtil import com.developerphil.adbidea.adb.command.receiver.PrintReceiver import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.developerphil.adbidea.ui.NotificationHelper.error -import com.intellij.ide.actions.OpenFileAction import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project +import org.jdesktop.swingx.util.OS import org.jetbrains.android.facet.AndroidFacet +import java.io.File import java.util.* import java.util.concurrent.TimeUnit @@ -18,7 +21,7 @@ import java.util.concurrent.TimeUnit * Description : */ -class ScreenRecordCommand(private val savePath: String, videoName: String, val length: Int, val showTouches: Boolean = false) : Command { +class ScreenRecordCommand(private val localPath: File, videoName: String, val length: Int, val showTouches:Boolean) : Command { lateinit var mDevice: IDevice val receiver = PrintReceiver() @@ -32,14 +35,30 @@ class ScreenRecordCommand(private val savePath: String, videoName: String, val l val timer = Timer() timer.schedule(object : TimerTask() { override fun run() { - device.pullFile(remotePath, savePath) - OpenFileAction.openFile(savePath, project) - //todo:need optimize - timer.schedule(object : TimerTask() { - override fun run() { + AdbUtil.pullFile(device,remotePath,localPath.absolutePath,object : SyncService.ISyncProgressMonitor{ + override fun startSubTask(p0: String?) { + + } + override fun start(p0: Int) { + + } + + override fun stop() { + if (OS.isWindows()) { + Runtime.getRuntime().exec(arrayOf("cmd", "/C", "start ${localPath.parentFile.absolutePath}")) + }else if (OS.isMacOSX()) { + Runtime.getRuntime().exec("open ${localPath.parentFile.absolutePath}") + } device.executeShellCommand("rm $remotePath",receiver,10L,TimeUnit.SECONDS) } - }, 5 * 1000L) + + override fun isCanceled(): Boolean = false + + override fun advance(p0: Int) { + + } + + }) } }, (length + 1) * 1000L) val string = receiver.toString() diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index dc29b0b3..818245b5 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -216,7 +216,7 @@ description="Input simple string to device"> Date: Sat, 13 Oct 2018 17:21:25 +0800 Subject: [PATCH 13/43] add monkey test and some comments --- README.md | 16 +- .../developerphil/adbidea/adb/AdbFacade.java | 9 +- .../ui/ApplicationManagementFrame.form | 46 ++-- .../ui/ApplicationManagementFrame.java | 66 +++-- .../adbidea/ui/BoundTableModel.java | 2 +- .../adbidea/ui/DeviceInfoFrame.java | 4 +- .../adbidea/ui/RecordOptionDialog.java | 6 +- .../kotlin/com/developerphil/adbidea/Const.kt | 2 +- .../developerphil/adbidea/HelperMethods.kt | 10 + .../adbidea/action/extend/PutStringAction.kt | 2 +- .../action/extend/ScreenCaptureAction.kt | 2 +- .../adb/command/ActivityServiceCommand.kt | 15 +- .../adb/command/CaptureScreenCommand.kt | 11 +- .../adb/command/CommonStringResultCommand.kt | 5 + .../adb/command/ForegroundActivityCommand.kt | 5 + .../adbidea/adb/command/MonkeyTestCommand.kt | 37 +++ .../adb/command/PackageDetailCommand.kt | 16 +- .../adbidea/adb/command/PackagePathCommand.kt | 18 +- .../adb/command/ScreenRecordCommand.kt | 10 +- .../adbidea/bean/BoundItemBean.kt | 2 - src/main/resources/META-INF/plugin.xml | 248 ++++++++++-------- src/main/resources/icon.png | Bin 33577 -> 31452 bytes 22 files changed, 324 insertions(+), 208 deletions(-) create mode 100644 src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt diff --git a/README.md b/README.md index 4902f690..13c0bb09 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,16 @@ The following commands are provided: * Clear App Data * Clear App Data and Restart + Start at 1.6: + +* Application management +* Application Interacting +* Show Device information +* Install apk files on your computer to your device +* Put simple string on your computer to your device +* Record device screen save to computer +* Capture device screen save to computer + Usage ===== @@ -50,13 +60,13 @@ License ======= Copyright 2017 Philippe Breault - + 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. diff --git a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java index 5d45cd4a..40c0607f 100644 --- a/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java +++ b/src/main/java/com/developerphil/adbidea/adb/AdbFacade.java @@ -16,6 +16,7 @@ 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.PutStringToDeviceCommand; @@ -143,6 +144,10 @@ public static void showForegroundActivity(Project project, Function1 callback) { + executeOnDevice(project, new MonkeyTestCommand(packageName,count,callback)); + } + public static void putStringToDevice(@Nullable Project project, @NotNull String str) { executeOnDevice(project, new PutStringToDeviceCommand(str)); } @@ -155,8 +160,8 @@ public static void getSimpleInfo(Project project, String command, String desc, F executeOnDevice(project, new CommonStringResultCommand(command, desc, callback)); } - public static void captureScreen(@Nullable Project project, String path) { - executeOnDevice(project, new CaptureScreenCommand(path)); + public static void captureScreen(@Nullable Project project, File file) { + executeOnDevice(project, new CaptureScreenCommand(file)); } public static void recordScreen(@Nullable Project project, File localFile, String videoName, int length, boolean showTouches) { diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form index 2852edb4..983ff10f 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form @@ -7,10 +7,8 @@ - - - + @@ -19,7 +17,7 @@ - + @@ -105,11 +103,9 @@ - - - - + + @@ -147,31 +143,29 @@ - + - - + + - + - + - - - + - + - + @@ -180,24 +174,26 @@ - + - - + + - + - + - + + + - + diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java index 01692e85..57f275ca 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java @@ -1,5 +1,6 @@ package com.developerphil.adbidea.ui; +import com.developerphil.adbidea.HelperMethodsKt; import com.developerphil.adbidea.adb.AdbFacade; import com.intellij.openapi.project.Project; import com.intellij.ui.JBColor; @@ -26,6 +27,7 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButton; @@ -53,20 +55,20 @@ public class ApplicationManagementFrame extends JFrame { private JCheckBox mShowInstallersCheckBox; private JCheckBox mContainsUninstalledCheckBox; private JButton mQueryButton; - private JTextField tv_keyword; - private JList mJList; - private JButton mUninstallButton; - private JButton mClearAppCacheDataButton; - private JButton mRunningServicesButton; - private JButton mViewDetailButton; - private JButton mViewPathButton; - private JPanel mPanel; + private JTextField tv_keyword; + private JList mJList; + private JButton mUninstallButton; + private JButton mClearAppCacheDataButton; + private JButton mRunningServicesButton; + private JButton mViewDetailButton; + private JButton mViewPathButton; + private JPanel mPanel; private JScrollPane sp; - private JTextPane tp; + private JTextPane tp; private JScrollPane sp_tp; - private JButton mForceStopButton; - private JButton mForegroundActivityButton; - private JButton mTrimMemoryButton; + private JButton mForceStopButton; + private JButton mForegroundActivityButton; + private JButton mMonkeyTestButton; private static final String PARAMETER_DISABLED = "-d "; private static final String PARAMETER_ENABLED = "-e "; @@ -176,7 +178,7 @@ 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); + Utils.append2TextPane("View " + name + "apk saveFile : \n", JBColor.BLUE, tp); AdbFacade.getPackagePath(mProject, name, s -> { Utils.append2TextPane(s, tp); return null; @@ -225,14 +227,33 @@ public void mouseClicked(MouseEvent e) { return null; }); }); - //mTrimMemoryButton.addActionListener(e -> { - // List selectedValuesList = mJList.getSelectedValuesList(); - // for (String packageName : selectedValuesList) { - // String name = getRealPackageName(packageName); - // Utils.append2TextPane("Trim Memory of " + name + ":\n", JBColor.BLUE); - // AdbFacade.trimMemory(mProject, name); - // } - //}); + mMonkeyTestButton.addActionListener(e -> { + List selectedValuesList = mJList.getSelectedValuesList(); + String name = ""; + if (!selectedValuesList.isEmpty()) { + name = getRealPackageName(selectedValuesList.get(0)); + Utils.append2TextPane("Monkey test of " + name + ":\n", JBColor.BLUE,tp); + } + String countStr = JOptionPane.showInputDialog("Enter test count(only integers):"); + if (countStr.isEmpty()) { + HelperMethodsKt.showErrorMsg("count can not empty"); + return; + } + int count = 0; + try { + count = Integer.parseInt(countStr); + } catch (NumberFormatException e1) { + HelperMethodsKt.showErrorMsg("parse count error,You can only enter integers"); + return; + } + if (count > 0) { + AdbFacade.monkeyTest(mProject, name,count, s -> { + Utils.append2TextPane(s, tp); + return null; + }); + } + + }); setContentPane($$$getRootComponent$$$()); } @@ -354,9 +375,6 @@ public static void main(String... args) { mForegroundActivityButton = new JButton(); mForegroundActivityButton.setText("Foreground Activity"); panel3.add(mForegroundActivityButton, cc.xy(1, 2)); - mTrimMemoryButton = new JButton(); - mTrimMemoryButton.setText("Trim memory"); - panel3.add(mTrimMemoryButton, cc.xy(2, 2)); sp = new JScrollPane(); sp.setMinimumSize(new Dimension(0, 100)); mPanel.add(sp, cc.xyw(1, 4, 4, CellConstraints.FILL, CellConstraints.FILL)); diff --git a/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java b/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java index 3889ef14..950a21bf 100644 --- a/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java +++ b/src/main/java/com/developerphil/adbidea/ui/BoundTableModel.java @@ -35,7 +35,7 @@ public void clear() { } public void addEmptyRow() { - data.add(new BoundItemBean(true, "", "",BoundDataType.NULL)); + data.add(new BoundItemBean(true, "", "",BoundDataType.STRING)); fireTableRowsInserted(data.size() - 1, data.size() - 1); } diff --git a/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java index 90e18410..83f44b51 100644 --- a/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/DeviceInfoFrame.java @@ -19,7 +19,7 @@ /** * Created by XQ Yang on 10/11/2018 11:45 AM. - * Description : + * Description : show some device information */ public class DeviceInfoFrame extends JFrame { @@ -120,11 +120,13 @@ public void mouseClicked(MouseEvent e) { if (verison >= 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); } else { getInfo2Show("IMEI 1 :", "dumpsys iphonesubinfo", "get IMEI 1 "); } } catch (NumberFormatException e1) { e1.printStackTrace(); + Utils.append2TextPaneNewLine("Can not get IMEI", JBColor.red, mTextPane); } } AdbFacade.getSimpleInfo(mProject, "ifconfig | grep Mask", "get IP Address ", s -> { diff --git a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java index ed8e369b..9e2dd907 100644 --- a/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java +++ b/src/main/java/com/developerphil/adbidea/ui/RecordOptionDialog.java @@ -20,7 +20,11 @@ import kotlin.Unit; import kotlin.jvm.functions.Function2; import org.jetbrains.annotations.NotNull; - +/** + * @describe + * @author Void Young + * @date 2018-10-13 16:48:56 + */ public class RecordOptionDialog extends JDialog { private JPanel contentPane; private JButton buttonOK; diff --git a/src/main/kotlin/com/developerphil/adbidea/Const.kt b/src/main/kotlin/com/developerphil/adbidea/Const.kt index 2b450d2b..16ac68bc 100644 --- a/src/main/kotlin/com/developerphil/adbidea/Const.kt +++ b/src/main/kotlin/com/developerphil/adbidea/Const.kt @@ -2,7 +2,7 @@ package com.developerphil.adbidea /** * Created by XQ Yang on 10/10/2018 3:28 PM. - * Description : + * Description : some actions form android.content.Intent */ val ACTION_MAIN = "android.intent.action.MAIN" diff --git a/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt b/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt index 34d650d2..c9fa7f0a 100644 --- a/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt +++ b/src/main/kotlin/com/developerphil/adbidea/HelperMethods.kt @@ -1,6 +1,7 @@ package com.developerphil.adbidea import com.intellij.openapi.application.ApplicationManager +import org.jdesktop.swingx.util.OS import javax.swing.JOptionPane fun waitUntil(timeoutMillis: Long = 30000L, step: Long = 100L, condition: () -> Boolean) { @@ -19,4 +20,13 @@ fun invokeLater(runnable: () -> Unit) { fun showErrorMsg(msg:String){ JOptionPane.showMessageDialog(null, msg,"Error", JOptionPane.ERROR_MESSAGE) +} + + +fun openFileExplorer(path: String){ + if (OS.isWindows()) { + Runtime.getRuntime().exec(arrayOf("cmd", "/C", "start $path")) + }else if (OS.isMacOSX()) { + Runtime.getRuntime().exec("open $path") + } } \ No newline at end of file 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 704ad52e..8fd6a32d 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/PutStringAction.kt @@ -15,7 +15,7 @@ class PutStringAction : AdbAction() { override fun actionPerformed(e: AnActionEvent?, project: Project?) { - var result = JOptionPane.showInputDialog("Input simple string put to device") + var result = JOptionPane.showInputDialog("Input simple string(ASCII) put to device") 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 9ae6f09d..7a363297 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt @@ -33,7 +33,7 @@ class ScreenCaptureAction : AdbAction() { .choose(project, selectedFile) if (choose.isNotEmpty()) { selectedFile = choose[0] - AdbFacade.captureScreen(project, File(selectedFile?.canonicalPath, "${deviceName}_${dateFormat.format(Date())}.png").absolutePath) + AdbFacade.captureScreen(project, File(selectedFile?.canonicalPath, "${deviceName}_${dateFormat.format(Date())}.png")) } } 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 ad44c44e..9df92196 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ActivityServiceCommand.kt @@ -9,20 +9,23 @@ import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet import java.util.concurrent.TimeUnit +/** + * @describe + * @author Void Young + * @date 2018-10-13 16:48:56 + */ class ActivityServiceCommand(private val mPackageName: String,private val callback:(String)->Unit) : Command { override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { - var packageName = packageName - packageName = mPackageName try { - if (packageName.isNotEmpty()&&!isAppInstalled(device, packageName)) { - error(String.format("%s is not installed on %s", packageName, device.name)) + if (mPackageName.isNotEmpty()&&!isAppInstalled(device, mPackageName)) { + error(String.format("%s is not installed on %s", mPackageName, device.name)) return false } val receiver = PrintReceiver() - device.executeShellCommand("dumpsys activity services $packageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s get activity service on %s", packageName, device.name)) + device.executeShellCommand("dumpsys activity services $mPackageName", receiver, 15L, TimeUnit.SECONDS) + 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 2a583299..0dbeb3f3 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CaptureScreenCommand.kt @@ -1,27 +1,28 @@ package com.developerphil.adbidea.adb.command import com.android.ddmlib.IDevice +import com.developerphil.adbidea.openFileExplorer import com.developerphil.adbidea.ui.NotificationHelper -import com.intellij.ide.actions.OpenFileAction import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jdesktop.swingx.util.OS import org.jetbrains.android.facet.AndroidFacet import java.io.BufferedReader +import java.io.File import java.io.InputStreamReader /** * Created by XQ Yang on 10/10/2018 10:56 AM. * Description : */ -class CaptureScreenCommand(val path: String) : Command { +class CaptureScreenCommand(val saveFile: File) : Command { override fun run(project: Project, device: IDevice?, facet: AndroidFacet?, packageName: String?): Boolean { try { val runtime = Runtime.getRuntime() val p: Process = if (OS.isWindows()) { - runtime.exec(arrayOf("cmd", "/C", "adb exec-out screencap -p > $path")) + runtime.exec(arrayOf("cmd", "/C", "adb exec-out screencap -p > ${saveFile.absolutePath}")) } else { - runtime.exec(arrayOf("/bin/sh","-c","adb exec-out screencap -p > $path")) + runtime.exec(arrayOf("/bin/sh","-c","adb exec-out screencap -p > ${saveFile.absolutePath}")) } val br = BufferedReader(InputStreamReader(p.inputStream)) var line = br.readLine() @@ -35,7 +36,7 @@ class CaptureScreenCommand(val path: String) : Command { notification.notify(project) } br.close() - OpenFileAction.openFile(path, project) + openFileExplorer(saveFile.parentFile.absolutePath) return true } catch (e1: Exception) { NotificationHelper.error("Capture Screen ... " + e1.message) 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 6cdfb07b..f914267c 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/CommonStringResultCommand.kt @@ -8,6 +8,11 @@ import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet import java.util.concurrent.TimeUnit +/** + * @describe + * @author Void Young + * @date 2018-10-13 16:48:56 + */ class CommonStringResultCommand(private val commandStr:String,private val operationDesc:String,private val callback:((String)->Unit)? = null) : Command { 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 608ae5dd..71e20e58 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ForegroundActivityCommand.kt @@ -8,6 +8,11 @@ import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet import java.util.concurrent.TimeUnit +/** + * @describe + * @author Void Young + * @date 2018-10-13 16:48:56 + */ class ForegroundActivityCommand(private val callback:(String)->Unit) : Command { diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt new file mode 100644 index 00000000..6a7d6cda --- /dev/null +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/MonkeyTestCommand.kt @@ -0,0 +1,37 @@ +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.intellij.notification.NotificationType +import com.intellij.openapi.project.Project +import org.jetbrains.android.facet.AndroidFacet +import java.util.concurrent.TimeUnit + +class MonkeyTestCommand(private val mPackageName: String, private val count: Int, private val callback: (String) -> Unit) : Command { + + override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { + try { + val receiver = PrintReceiver() + val sb = StringBuilder("monkey ") + if (mPackageName.isNotEmpty()) { + sb.append("-p $mPackageName ") + }else if (packageName.isNotEmpty()) { + sb.append("-p $packageName ") + } + sb.append("-v $count") + device.executeShellCommand(sb.toString(), receiver, 15L, TimeUnit.SECONDS) + 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) + notification.notify(project) + return true + } catch (e1: Exception) { + error("Start monkey test... " + e1.message) + } + return false + } + +} 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 03ceaa9b..e64a1d1a 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackageDetailCommand.kt @@ -10,23 +10,27 @@ import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet import java.util.concurrent.TimeUnit +/** + * @describe + * @author Void Young + * @date 2018-10-13 16:48:56 + */ + class PackageDetailCommand(private val mPackageName: String,private val callback:(String)->Unit) : Command { override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { - var packageName = packageName - packageName = mPackageName try { - if (isAppInstalled(device, packageName)) { + if (isAppInstalled(device, mPackageName)) { val receiver = PrintReceiver() - device.executeShellCommand("dumpsys package $packageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s get package detail on %s", packageName, device.name)) + device.executeShellCommand("dumpsys package $mPackageName", receiver, 15L, TimeUnit.SECONDS) + 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) notification.notify(project) return true } else { - error(String.format("%s is not installed on %s", packageName, device.name)) + error(String.format("%s is not installed on %s", mPackageName, device.name)) } } catch (e1: Exception) { error("Get package detail... " + e1.message) 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 4d55cbdc..5fd05b22 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt @@ -10,26 +10,30 @@ import com.intellij.openapi.project.Project import org.jetbrains.android.facet.AndroidFacet import java.util.concurrent.TimeUnit +/** + * @describe + * @author Void Young + * @date 2018-10-13 16:48:56 + */ + class PackagePathCommand(private val mPackageName: String,private val callback:(String)->Unit) : Command { override fun run(project: Project, device: IDevice, facet: AndroidFacet, packageName: String): Boolean { - var packageName = packageName - packageName = mPackageName try { - if (isAppInstalled(device, packageName)) { + if (isAppInstalled(device, mPackageName)) { val receiver = PrintReceiver() - device.executeShellCommand("pm path $packageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s get package path on %s", packageName, device.name)) + device.executeShellCommand("pm saveFile $mPackageName", receiver, 15L, TimeUnit.SECONDS) + info(String.format("%s get package saveFile on %s", mPackageName, device.name)) val string = receiver.toString() callback.invoke(string) val notification = NotificationHelper.INFO.createNotification("ADB IDEA", string, NotificationType.INFORMATION, NOOP_LISTENER) notification.notify(project) return true } else { - error(String.format("%s is not installed on %s", packageName, device.name)) + error(String.format("%s is not installed on %s", mPackageName, device.name)) } } catch (e1: Exception) { - error("Get package path... " + e1.message) + error("Get package saveFile... " + e1.message) } return false diff --git a/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt b/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt index 1159ff19..bd2cc9bd 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/ScreenRecordCommand.kt @@ -5,12 +5,12 @@ import com.android.ddmlib.ScreenRecorderOptions import com.android.ddmlib.SyncService import com.developerphil.adbidea.adb.AdbUtil import com.developerphil.adbidea.adb.command.receiver.PrintReceiver +import com.developerphil.adbidea.openFileExplorer import com.developerphil.adbidea.ui.NotificationHelper import com.developerphil.adbidea.ui.NotificationHelper.NOOP_LISTENER import com.developerphil.adbidea.ui.NotificationHelper.error import com.intellij.notification.NotificationType import com.intellij.openapi.project.Project -import org.jdesktop.swingx.util.OS import org.jetbrains.android.facet.AndroidFacet import java.io.File import java.util.* @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit /** * Created by XQ Yang on 2018-10-9 15:00:43 - * Description : + * Description : record screen to computer */ class ScreenRecordCommand(private val localPath: File, videoName: String, val length: Int, val showTouches:Boolean) : Command { @@ -44,11 +44,7 @@ class ScreenRecordCommand(private val localPath: File, videoName: String, val le } override fun stop() { - if (OS.isWindows()) { - Runtime.getRuntime().exec(arrayOf("cmd", "/C", "start ${localPath.parentFile.absolutePath}")) - }else if (OS.isMacOSX()) { - Runtime.getRuntime().exec("open ${localPath.parentFile.absolutePath}") - } + openFileExplorer(localPath.parentFile.absolutePath) device.executeShellCommand("rm $remotePath",receiver,10L,TimeUnit.SECONDS) } diff --git a/src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt b/src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt index 3bb53c9f..da062574 100644 --- a/src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt +++ b/src/main/kotlin/com/developerphil/adbidea/bean/BoundItemBean.kt @@ -18,8 +18,6 @@ data class BoundItemBean(var selected: Boolean, var key: String, var value: Stri } - - enum class BoundDataType(val prefix:String){ STRING("--es"), BOOLEAN("--ez"), diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 818245b5..5a33bee3 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,10 +1,10 @@ - com.developerphil.adbidea - ADB Idea - 1.5.2 - Philippe Breault + com.developerphil.adbidea + ADB Idea + 1.6.0 + Philippe Breault -
    • ADB Uninstall App
    • @@ -16,6 +16,13 @@
    • ADB Revoke Permissions
    • ADB Start App With Debugger
    • ADB Restart App With Debugger
    • +
    • ADB Application management
    • +
    • ADB Application Interacting
    • +
    • ADB Device information
    • +
    • ADB Install Apk file
    • +
    • ADB Put simple string to device
    • +
    • ADB Record device screen
    • +
    • ADB Capture device screen

    There are two basic ways to invoke a command: @@ -26,7 +33,18 @@ ]]>
    - 1.6.0 +
      +
    • Add some simple funcation
    • +
    • ADB Application management
    • +
    • ADB Application Interacting
    • +
    • ADB Device information
    • +
    • ADB Install Apk file
    • +
    • ADB Put simple string to device
    • +
    • ADB Record device screen
    • +
    • ADB Capture device screen
    • +
    1.5.2
    • BUGFIX: Show the name of the devices in addition to the serial number when multiple devices are connected
    • @@ -113,123 +131,123 @@
    • Now support projects with more than one application module
    ]]> -
    + - - - - + + + + - com.intellij.modules.platform - org.jetbrains.android + com.intellij.modules.platform + org.jetbrains.android - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - com.developerphil.adbidea.ObjectGraph - - + + + com.developerphil.adbidea.ObjectGraph + +
    \ No newline at end of file diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png index 321ce39001f513793dd946823fadf0e3bc60b99a..e37a2347af482ce54f9fcfe7aa9ae4adb7be27ed 100644 GIT binary patch literal 31452 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 IaSs*pKc5nSA^-pY literal 33577 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 Date: Sun, 14 Oct 2018 10:28:32 +0800 Subject: [PATCH 14/43] fix bug and optimize --- .../adb/command/receiver/PrintReceiver.java | 9 +++------ .../adbidea/ui/ApplicationManagementFrame.form | 4 ++-- .../adbidea/ui/ApplicationManagementFrame.java | 14 +++++++++----- .../adbidea/action/extend/ScreenCaptureAction.kt | 2 +- .../adbidea/action/extend/ScreenRecordAction.kt | 7 ++++--- .../adbidea/adb/command/PackagePathCommand.kt | 6 +++--- src/main/resources/META-INF/plugin.xml | 4 +++- 7 files changed, 25 insertions(+), 21 deletions(-) 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 index 5e3ffa83..c04da936 100644 --- a/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java +++ b/src/main/java/com/developerphil/adbidea/adb/command/receiver/PrintReceiver.java @@ -1,18 +1,17 @@ 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"; + mString = new String(data, offset, length, Charsets.UTF_8) + "\r\n"; } - } @Override @@ -28,10 +27,8 @@ public boolean isCancelled() { return false; } - - @Override public String toString() { - return mString; + return Utils.isEmpty(mString) ? "" : mString; } } diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form index 983ff10f..82f9ab81 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.form @@ -4,7 +4,7 @@ - + @@ -17,7 +17,7 @@ - + diff --git a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java index 57f275ca..2315d57f 100644 --- a/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java +++ b/src/main/java/com/developerphil/adbidea/ui/ApplicationManagementFrame.java @@ -178,7 +178,7 @@ public void mouseClicked(MouseEvent e) { List selectedValuesList = mJList.getSelectedValuesList(); for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - Utils.append2TextPane("View " + name + "apk saveFile : \n", JBColor.BLUE, tp); + Utils.append2TextPane("View " + name + " apk Path : \n", JBColor.BLUE, tp); AdbFacade.getPackagePath(mProject, name, s -> { Utils.append2TextPane(s, tp); return null; @@ -190,7 +190,7 @@ public void mouseClicked(MouseEvent e) { if (selectedValuesList.isEmpty()) { String keywordText = tv_keyword.getText(); if (!Utils.isEmpty(keywordText)) { - Utils.append2TextPane("View running services related with" + keywordText + " : \n", JBColor.BLUE, tp); + Utils.append2TextPane("View running services related with " + keywordText + " : \n", JBColor.BLUE, tp); AdbFacade.getActivityService(mProject, keywordText, s -> { Utils.append2TextPane(s, tp); return null; @@ -205,7 +205,7 @@ public void mouseClicked(MouseEvent e) { } for (String packageName : selectedValuesList) { String name = getRealPackageName(packageName); - Utils.append2TextPane("View running services related with" + name + " : \n", JBColor.BLUE, tp); + Utils.append2TextPane("View running services related with " + name + " : \n", JBColor.BLUE, tp); AdbFacade.getActivityService(mProject, name, s -> { Utils.append2TextPane(s, tp); return null; @@ -223,7 +223,11 @@ public void mouseClicked(MouseEvent e) { mForegroundActivityButton.addActionListener(e -> { Utils.append2TextPane("Foreground Activity : \n", JBColor.BLUE, tp); AdbFacade.showForegroundActivity(mProject, s -> { - Utils.append2TextPane(s, tp); + if (Utils.isEmpty(s)) { + Utils.append2TextPaneNewLine("get foreground Activity failure",JBColor.RED, tp); + } else { + Utils.append2TextPaneNewLine(s, tp); + } return null; }); }); @@ -232,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.append2TextPane("Monkey test of " + name + " :\n", JBColor.BLUE,tp); } String countStr = JOptionPane.showInputDialog("Enter test count(only integers):"); if (countStr.isEmpty()) { 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 7a363297..90a1ad96 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenCaptureAction.kt @@ -19,7 +19,7 @@ import java.util.* class ScreenCaptureAction : AdbAction() { var deviceName = "" init { - saveDirChooserDescriptor.title = "Select capture png file save to..." + saveDirChooserDescriptor.title = "Select capture .png file save to..." } 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 083034d8..b713f297 100644 --- a/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt +++ b/src/main/kotlin/com/developerphil/adbidea/action/extend/ScreenRecordAction.kt @@ -18,7 +18,8 @@ import java.util.* * Description : record device screen via adb */ class ScreenRecordAction : AdbAction() { - var deviceName = "" + private var deviceName = "Android" + init { saveDirChooserDescriptor.title = "Select record .mp4 file save to..." } @@ -33,10 +34,10 @@ class ScreenRecordAction : AdbAction() { val choose = FileChooserDialogImpl(saveDirChooserDescriptor, project) .choose(project, selectedFile) if (choose.isNotEmpty()) { - val dialog = RecordOptionDialog { showTouches,length-> + val dialog = RecordOptionDialog { showTouches, length -> selectedFile = choose[0] val videoName = "${deviceName}_${dateFormat.format(Date())}.mp4" - AdbFacade.recordScreen(project, File(selectedFile?.canonicalPath, videoName),videoName,length,showTouches) + AdbFacade.recordScreen(project, File(selectedFile?.canonicalPath, videoName), videoName, length, showTouches) } dialog.pack() dialog.isVisible = true 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 5fd05b22..3bd42039 100644 --- a/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt +++ b/src/main/kotlin/com/developerphil/adbidea/adb/command/PackagePathCommand.kt @@ -22,8 +22,8 @@ class PackagePathCommand(private val mPackageName: String,private val callback:( try { if (isAppInstalled(device, mPackageName)) { val receiver = PrintReceiver() - device.executeShellCommand("pm saveFile $mPackageName", receiver, 15L, TimeUnit.SECONDS) - info(String.format("%s get package saveFile on %s", mPackageName, device.name)) + device.executeShellCommand("pm path $mPackageName", receiver, 15L, TimeUnit.SECONDS) + 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) @@ -33,7 +33,7 @@ class PackagePathCommand(private val mPackageName: String,private val callback:( error(String.format("%s is not installed on %s", mPackageName, device.name)) } } catch (e1: Exception) { - error("Get package saveFile... " + e1.message) + error("Get package path... " + e1.message) } return false diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 5a33bee3..793080ae 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -36,7 +36,8 @@ 1.6.0
        -
      • Add some simple funcation
      • +
      • Add some simple function
      • +
        • ADB Application management
        • ADB Application Interacting
        • ADB Device information
        • @@ -45,6 +46,7 @@
        • ADB Record device screen
        • ADB Capture device screen
        +
      1.5.2