├── QtAdb ├── AdbDevice.cpp ├── QtAdb.qrc ├── main.cpp ├── PsDlg.cpp ├── PsDlg.h ├── QtAdb.vcxproj.filters ├── QtAdb.cpp ├── PsDlg.ui ├── QtAdb.vcxproj ├── AdbDevice.h ├── QtAdb.h └── QtAdb.ui ├── README.md ├── QtAdb.sln ├── .gitattributes └── .gitignore /QtAdb/AdbDevice.cpp: -------------------------------------------------------------------------------- 1 | #include "AdbDevice.h" 2 | -------------------------------------------------------------------------------- /QtAdb/QtAdb.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | C:/Windows/xsbao.ico 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 基于Qt实现的adb-gui辅助工具,已实现了文件树显示、进程树显示、APP管理基本功能,欢迎使用,欢迎star! 3 | 4 | ![image](https://user-images.githubusercontent.com/53322237/72068490-228f2d00-3320-11ea-9f7d-a44a05c8b116.png) -------------------------------------------------------------------------------- /QtAdb/main.cpp: -------------------------------------------------------------------------------- 1 | #include "QtAdb.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | QtAdb w; 8 | w.show(); 9 | return a.exec(); 10 | } 11 | -------------------------------------------------------------------------------- /QtAdb/PsDlg.cpp: -------------------------------------------------------------------------------- 1 | #include "PsDlg.h" 2 | #include "QtAdb.h" 3 | 4 | PsDlg::PsDlg(QWidget *parent, AdbDevice *cd, int pid) 5 | : QDialog(parent), cd(cd), pid(pid) 6 | { 7 | ui.setupUi(this); 8 | 9 | onTabChanged(ui.tabWidget->currentIndex()); 10 | TableFilter::install(ui.tableMemory); 11 | } 12 | 13 | PsDlg::~PsDlg() 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /QtAdb.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.960 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QtAdb", "QtAdb\QtAdb.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64 15 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.Build.0 = Debug|x64 16 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|x64 17 | {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {F312CB24-6BC6-4F53-B7B2-9739C78A8201} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /QtAdb/PsDlg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "ui_PsDlg.h" 5 | 6 | #include "AdbDevice.h" 7 | 8 | class PsDlg : public QDialog 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | PsDlg(QWidget *parent, AdbDevice *cd, int pid); 14 | ~PsDlg(); 15 | 16 | void updateMemory() 17 | { 18 | int i = 0; 19 | for (auto l : cd->shell({ "cat", "/proc/" + QString::number(pid) + "/maps" }, true)) 20 | { 21 | LineParser p(std::move(l)); 22 | auto addr = p.next().split("-"); 23 | auto& begin = addr[0]; 24 | auto& end = addr[1]; 25 | auto flag = p.next(); 26 | auto size = p.next(); 27 | auto f1 = p.next(); 28 | auto f2 = p.next(); 29 | auto image = p.rest(); 30 | 31 | ui.tableMemory->insertRow(i); 32 | ui.tableMemory->setItem(i, 0, new QTableWidgetItem(begin)); 33 | ui.tableMemory->setItem(i, 1, new QTableWidgetItem(end)); 34 | ui.tableMemory->setItem(i, 2, new QTableWidgetItem(flag)); 35 | ui.tableMemory->setItem(i, 3, new QTableWidgetItem(size)); 36 | ui.tableMemory->setItem(i, 4, new QTableWidgetItem(f1)); 37 | ui.tableMemory->setItem(i, 5, new QTableWidgetItem(f2)); 38 | ui.tableMemory->setItem(i, 6, new QTableWidgetItem(image)); 39 | 40 | ++i; 41 | } 42 | } 43 | 44 | void updateThread() 45 | { 46 | int i = 0; 47 | for (auto l : cd->shell({ "cat", "/proc/" + QString::number(pid) + "/task" }, true)) 48 | { 49 | } 50 | } 51 | 52 | void updateStatus() 53 | { 54 | ui.textStatus->setPlainText(cd->shell({ "cat", "/proc/" + QString::number(pid) + "/status" }, true)); 55 | } 56 | 57 | public slots: 58 | void onTabChanged(int i) 59 | { 60 | auto label = ui.tabWidget->tabText(i); 61 | if (label == "状态") 62 | updateStatus(); 63 | if (label == "内存") 64 | updateMemory(); 65 | if (label == "线程") 66 | updateThread(); 67 | } 68 | 69 | private: 70 | Ui::PsDlg ui; 71 | 72 | AdbDevice *cd = nullptr; 73 | int pid; 74 | }; 75 | -------------------------------------------------------------------------------- /QtAdb/QtAdb.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} 14 | qrc;* 15 | false 16 | 17 | 18 | {99349809-55BA-4b9d-BF79-8FDBB0286EB3} 19 | ui 20 | 21 | 22 | {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} 23 | qrc;* 24 | false 25 | 26 | 27 | 28 | 29 | Source Files 30 | 31 | 32 | Source Files 33 | 34 | 35 | Source Files 36 | 37 | 38 | Source Files 39 | 40 | 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | 50 | 51 | Form Files 52 | 53 | 54 | Form Files 55 | 56 | 57 | 58 | 59 | Resource Files 60 | 61 | 62 | 63 | 64 | Header Files 65 | 66 | 67 | -------------------------------------------------------------------------------- /QtAdb/QtAdb.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "QtAdb.h" 3 | 4 | QtAdb::QtAdb(QWidget *parent) 5 | : QMainWindow(parent) 6 | { 7 | ui.setupUi(this); 8 | 9 | // 替换ComboBox组件 10 | comboDevice = new DeviceComboBox(this); 11 | ui.layout1->replaceWidget(ui.comboDevice, comboDevice); 12 | delete ui.comboDevice; 13 | 14 | // 应用列表 15 | ui.appList->setColumnWidth(0, 350); 16 | ui.appList->addAction(ui.actionStart); 17 | ui.appList->addAction(ui.actionStop); 18 | ui.appList->addAction(ui.actionUninstall); 19 | ui.appList->addAction(ui.actionClearSelect); 20 | ui.appList->addAction(ui.actionUpdateAppList); 21 | ui.appList->addAction(ui.actionDumpService); 22 | ui.appList->addAction(ui.actionDumpPackage); 23 | 24 | // 文件列表 25 | ui.tableFs->setColumnWidth(0, 280); 26 | ui.tableFs->setColumnWidth(1, 90); 27 | ui.tableFs->setColumnWidth(2, 80); 28 | ui.tableFs->setColumnWidth(3, 80); 29 | ui.tableFs->setColumnWidth(4, 80); 30 | 31 | // 文件树 32 | ui.treePs->setColumnWidth(0, 350); 33 | ui.treePs->setColumnWidth(1, 80); 34 | 35 | // 图标设置 36 | auto style = QApplication::style(); 37 | setWindowIcon(style->standardIcon(QStyle::SP_TitleBarMenuButton)); 38 | ui.actionStart->setIcon(style->standardIcon(QStyle::SP_MediaPlay)); 39 | ui.actionStop->setIcon(style->standardIcon(QStyle::SP_MediaStop)); 40 | ui.actionClearSelect->setIcon(style->standardIcon(QStyle::SP_DialogResetButton)); 41 | ui.actionUninstall->setIcon(style->standardIcon(QStyle::SP_MessageBoxCritical)); 42 | ui.actionUpdateAppList->setIcon(style->standardIcon(QStyle::SP_BrowserReload)); 43 | 44 | // 分割比例 45 | ui.splitter_v->setStretchFactor(0, 2); 46 | ui.splitter_v->setStretchFactor(1, 1); 47 | ui.splitter_h->setStretchFactor(0, 1); 48 | ui.splitter_h->setStretchFactor(1, 2); 49 | ui.splitterFs->setStretchFactor(0, 1); 50 | ui.splitterFs->setStretchFactor(1, 4); 51 | 52 | connect(comboDevice, &DeviceComboBox::deviceChanged, this, &QtAdb::changeDevice); 53 | 54 | // 添加表格的过滤功能 55 | TableFilter::install(ui.appList); 56 | TableFilter::install(ui.treePs); 57 | TableFilter::install(ui.tableFs); 58 | 59 | // 功能数据初始化 60 | comboDevice->updateDevices(); 61 | } 62 | 63 | QStringList QtAdb::getPath(QTreeWidgetItem *item) 64 | { 65 | QStringList path; 66 | for (auto p = item; p; p = p->parent()) 67 | path.push_front(p->text(0)); 68 | return path; 69 | } 70 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /QtAdb/PsDlg.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PsDlg 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1243 10 | 841 11 | 12 | 13 | 14 | 进程 15 | 16 | 17 | 18 | 19 | 20 | 1 21 | 22 | 23 | 24 | 状态 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 内存 35 | 36 | 37 | 38 | 39 | 40 | selection-background-color: rgb(204, 232, 255); 41 | selection-color: rgb(0, 0, 0); 42 | 43 | 44 | QAbstractItemView::SingleSelection 45 | 46 | 47 | QAbstractItemView::SelectRows 48 | 49 | 50 | false 51 | 52 | 53 | true 54 | 55 | 56 | false 57 | 58 | 59 | 20 60 | 61 | 62 | 20 63 | 64 | 65 | 66 | 起始 67 | 68 | 69 | 70 | 71 | 结束 72 | 73 | 74 | 75 | 76 | 保护 77 | 78 | 79 | 80 | 81 | vm_pgoff 82 | 83 | 84 | 85 | 86 | 设备号 87 | 88 | 89 | 90 | 91 | 节点号 92 | 93 | 94 | 95 | 96 | 文件名 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 线程 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | tabWidget 116 | currentChanged(int) 117 | PsDlg 118 | onTabChanged(int) 119 | 120 | 121 | 252 122 | 26 123 | 124 | 125 | 429 126 | -32 127 | 128 | 129 | 130 | 131 | 132 | onTabChanged(int) 133 | 134 | 135 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /QtAdb/QtAdb.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {B12702AD-ABFB-343A-A199-8E24837244A3} 15 | QtVS_v301 16 | 10.0.17763.0 17 | 18 | 19 | 20 | Application 21 | v141 22 | 23 | 24 | Application 25 | v141 26 | 27 | 28 | 29 | $(MSBuildProjectDirectory)\QtMsBuild 30 | 31 | 32 | $(SolutionDir)$(Platform)\$(Configuration)\ 33 | 34 | 35 | $(SolutionDir)$(Platform)\$(Configuration)\ 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 5.13-x64 54 | core;gui;widgets 55 | 56 | 57 | 5.13-x64 58 | core;gui;widgets 59 | 60 | 61 | 62 | 63 | 64 | 65 | true 66 | Disabled 67 | ProgramDatabase 68 | MultiThreadedDebugDLL 69 | true 70 | /utf-8 %(AdditionalOptions) 71 | 72 | 73 | Windows 74 | $(OutDir)\$(ProjectName).exe 75 | true 76 | 77 | 78 | 79 | 80 | true 81 | 82 | MultiThreadedDLL 83 | true 84 | /utf-8 %(AdditionalOptions) 85 | 86 | 87 | Windows 88 | $(OutDir)\$(ProjectName).exe 89 | false 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /QtAdb/AdbDevice.h: -------------------------------------------------------------------------------- 1 | #ifndef __ADBDEVICE_H__ 2 | #define __ADBDEVICE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #pragma comment(lib, "Shcore.lib") 24 | 25 | using namespace std; 26 | using namespace chrono; 27 | 28 | struct Region 29 | { 30 | float x1, x2; 31 | float y1, y2; 32 | 33 | Region(): x1(0.0), x2(1.0), y1(0.0), y2(1.0) {} 34 | 35 | Region& top(float n) { y2 = n; return *this; } 36 | Region& bot(float n) { y1 = 1.0 - n; return *this; } 37 | Region& left(float n) { x1 = n; return *this; } 38 | Region& right(float n) { x2 = 1.0 - n; return *this; } 39 | }; 40 | 41 | // 控件定位器 42 | struct CtrlLocator 43 | { 44 | QString text; 45 | QString cls; 46 | QString rc; 47 | 48 | CtrlLocator(const QString& name, const QString& ty = "", const QString& rc = "") 49 | : text(name), cls(ty), rc(rc) {} 50 | 51 | CtrlLocator(const char *name, const char *ty = "", const char *rc = "") 52 | : text(name), cls(ty), rc(rc) {} 53 | 54 | static CtrlLocator by_rc(const QString& name) { return CtrlLocator("", "", name); } 55 | }; 56 | 57 | struct ShellResult: public QByteArray 58 | { 59 | ShellResult(QByteArray&& data): QByteArray(data) {} 60 | 61 | // 所有输出转换成字符串 62 | inline operator QString() 63 | { 64 | return QString::fromUtf8(*this); 65 | } 66 | 67 | struct iter 68 | { 69 | const QByteArray *arr; 70 | int next_b = 0; 71 | int next_e; 72 | 73 | iter(const ShellResult *a, int size) : arr(a) 74 | { 75 | next_b = next_e = size; 76 | } 77 | 78 | iter(const ShellResult *a): arr(a) 79 | { 80 | next_e = arr->indexOf("\r\n", next_b); 81 | } 82 | 83 | iter& operator++() 84 | { 85 | next_b = next_e + 2; 86 | next_e = arr->indexOf("\r\n", next_b); 87 | return *this; 88 | } 89 | 90 | //后缀自加 91 | iter operator++(int) 92 | { 93 | auto r = *this; 94 | ++*this; 95 | return r; 96 | } 97 | 98 | QString operator*() const 99 | { 100 | return QString::fromUtf8(arr->data() + next_b, next_e - next_b); 101 | } 102 | 103 | bool operator==(const iter &arg) const 104 | { 105 | return next_b == arg.next_b || next_e < 0; 106 | } 107 | 108 | bool operator!=(const iter &arg) const 109 | { 110 | return !(arg == *this); 111 | } 112 | }; 113 | 114 | iter begin() const { return iter(this); } 115 | iter end() const { return iter(this, this->size()); } 116 | 117 | QStringList split() 118 | { 119 | auto l = ((QString)(*this)).split("\r\n"); 120 | if (l.size() > 0 && l.last().size() == 0) 121 | l.pop_back(); // 去除最后的空行 122 | return l; 123 | } 124 | }; 125 | 126 | struct LineParser: public QString 127 | { 128 | LineParser(QString&& str): QString(str) {} 129 | 130 | // 一串连续的非空格字符串 131 | QString next() 132 | { 133 | skip_white(); 134 | auto pos = cur_pos; 135 | auto& p = cur_pos; 136 | while (p < size() && !at(p).isSpace()) ++p; 137 | return mid(pos, p - pos); 138 | } 139 | 140 | QString readTo(QChar c) 141 | { 142 | skip_white(); 143 | auto pos = cur_pos; 144 | auto& p = cur_pos; 145 | while (p < size() && at(p) != c) ++p; 146 | return mid(pos, ++p - pos); 147 | } 148 | 149 | QString psname() 150 | { 151 | skip_white(); 152 | return at(cur_pos) == '[' ? readTo(']') : next(); 153 | } 154 | 155 | QString rest(bool skip = true) 156 | { 157 | if (skip) skip_white(); 158 | return mid(cur_pos); 159 | } 160 | 161 | void skip_white() 162 | { 163 | while (cur_pos < size() && at(cur_pos).isSpace()) ++cur_pos; 164 | } 165 | 166 | private: 167 | int cur_pos = 0; 168 | }; 169 | 170 | // 模拟器 171 | class AdbDevice 172 | { 173 | public: 174 | // 执行adb命令,并返回输出结果(二进制) 175 | static ShellResult adb(const QStringList& args) 176 | { 177 | QProcess p; 178 | p.start("adb.exe", args); 179 | p.waitForFinished(); 180 | return p.readAll(); 181 | } 182 | 183 | static QStringList adb_l(const QStringList& args) 184 | { 185 | return adb(args).split(); 186 | } 187 | 188 | QStringList adb_shell(const QStringList& args) 189 | { 190 | return adb_l(QStringList{"-s", name, "shell"} + args); 191 | } 192 | 193 | ShellResult shell(const QStringList& args, bool root = false) 194 | { 195 | return root ? adb(QStringList{"-s", name, "shell", "su", "-c"} + args) 196 | : adb(QStringList{"-s", name, "shell"} + args); 197 | } 198 | 199 | // 设备型号 200 | QString model() 201 | { 202 | return shell({ "getprop", "ro.product.model" }).trimmed(); 203 | } 204 | 205 | // 设备列表 206 | static QList devices() 207 | { 208 | auto l = adb_l(QStringList{ "devices" }); 209 | QList result; 210 | for (int i = 1; i < l.size(); ++i) 211 | { 212 | auto sl = l[i].split(QRegExp("\\s+")); 213 | if (sl.size() > 1) result.push_back(sl[0]); 214 | } 215 | return result; 216 | } 217 | 218 | AdbDevice(const QString& name): name(name) {} 219 | 220 | // 输入文字 221 | void input(const QString& text) 222 | { 223 | //LdConsole::ldconsole(QStringList{ 224 | // "action", "--index", index(), 225 | // "--key", "call.input", "--value", text 226 | //}); 227 | } 228 | 229 | struct EmuPoint 230 | { 231 | int x, y; 232 | }; 233 | 234 | // 点击屏幕 235 | void tap(const EmuPoint& p) 236 | { 237 | adb_shell(QStringList{ 238 | "input", "tap", 239 | QString::number(p.x), QString::number(p.y), 240 | }); 241 | } 242 | 243 | // 点击屏幕 244 | inline void tap(int x, int y) { tap(EmuPoint { x, y }); } 245 | 246 | // 控件边界 247 | struct CtrlBound 248 | { 249 | int left, top, right, bottom; 250 | 251 | CtrlBound(): left(0), top(0), right(0), bottom(0) {} 252 | 253 | operator bool() { return right > left && bottom > top; } 254 | 255 | EmuPoint center() const { return EmuPoint {(right + left) / 2, (bottom + top) / 2}; } 256 | }; 257 | 258 | // 点击屏幕 259 | inline void tap(const CtrlBound& b) { tap(b.center()); } 260 | 261 | // 寻找控件位置 262 | CtrlBound find_ctrl(const CtrlLocator& l) 263 | { 264 | CtrlBound bound; 265 | 266 | // QRegExp reg(R"(to:\s*(\S+)$)"); 267 | //auto s = adb_shell(QStringList {"uiautomator", "dump", "--compressed"}); 268 | // if (!(s.size() && s[0].indexOf(reg) > 0)) 269 | // return bound; 270 | 271 | // auto xml_path = reg.cap(1); 272 | // qDebug() << "xml path: " << xml_path; 273 | 274 | auto xml_path = "/sdcard/window_dump.xml"; 275 | 276 | adb_shell(QStringList {"uiautomator", "dump", "--compressed"}); 277 | auto data = adb_shell(QStringList {"cat", xml_path, ";", "rm", xml_path}); 278 | if (data.isEmpty()) return bound; 279 | 280 | QString result; 281 | QXmlStreamReader xml(data[0]); 282 | while (!xml.atEnd() && !xml.hasError()) 283 | { 284 | //读取下一个element. 285 | QXmlStreamReader::TokenType token = xml.readNext(); 286 | //如果获取了StartElement,则尝试读取 287 | if (token == QXmlStreamReader::StartElement && xml.name() == "node") 288 | { 289 | QXmlStreamAttributes attr = xml.attributes(); 290 | 291 | // ResouceID 单独定位 292 | if (l.rc.size() > 0) 293 | { 294 | if (attr.value("resource-id").toString() == l.rc) 295 | { 296 | result = attr.value("bounds").toString(); break; 297 | } 298 | else continue; 299 | } 300 | 301 | // 结合class和text定位 302 | if (l.cls.size() > 0) 303 | { 304 | if (attr.value("class").toString() != l.cls) continue; 305 | } 306 | if (attr.value("text").toString() == l.text) 307 | { 308 | result = attr.value("bounds").toString(); break; 309 | } 310 | } 311 | } 312 | 313 | if (result.size() > 0) 314 | { 315 | QRegExp reg(R"(\[(\d+),(\d+)\]\[(\d+),(\d+)\])"); 316 | auto pos = result.indexOf(reg); // TODO: assert(pos >= 0) 317 | bound.left = reg.cap(1).toInt(); 318 | bound.top = reg.cap(2).toInt(); 319 | bound.right = reg.cap(3).toInt(); 320 | bound.bottom = reg.cap(4).toInt(); 321 | } 322 | return bound; 323 | } 324 | 325 | // 等待某个控件出现在当前页面 326 | CtrlBound wait_ctrl(const CtrlLocator& l, int ms = 100 * 1000) 327 | { 328 | auto t = system_clock::now(); 329 | CtrlBound bound; 330 | do { 331 | if (bound = find_ctrl(l)) break; 332 | } while (duration_cast(system_clock::now() - t).count() < ms); 333 | return bound; 334 | } 335 | 336 | // 寻找控件位置 337 | CtrlBound click_ctrl(const QString& text, int ms = 100 * 1000) 338 | { 339 | auto t = system_clock::now(); 340 | auto bound = wait_ctrl(text, ms); 341 | if (bound) { tap(bound.center()); } 342 | return bound; 343 | } 344 | 345 | // 寻找控件位置 346 | CtrlBound click_rc(const QString& rc, int ms = 100 * 1000) 347 | { 348 | auto bound = wait_ctrl(CtrlLocator::by_rc(rc), ms); 349 | if (bound) tap(bound.center()); 350 | return bound; 351 | } 352 | 353 | // 启动应用 354 | // com.ss.android.ugc.live/com.ss.android.ugc.live.main.MainActivity 355 | void start_app(const QString& pkg_act) 356 | { 357 | adb_shell(QStringList {"am", "start", "-n", pkg_act}); 358 | } 359 | 360 | // 获取当前Activity 361 | QString activity(QString* pkg = nullptr) 362 | { 363 | QString result; 364 | auto out = adb_shell(QStringList{"dumpsys window w | grep name="}); 365 | for (int i = 0; i < out.size() - 1; ++i) 366 | if (out[i].contains("name=StatusBar")) 367 | { 368 | QRegExp reg(R"(name=([\w\.]+)\/([\w\.]+))"); 369 | auto w = out[i + 1]; 370 | if (w.indexOf(reg) > 0) 371 | { 372 | if (pkg) *pkg = reg.cap(1); 373 | result = reg.cap(2); 374 | break; 375 | } 376 | } 377 | return result; 378 | } 379 | 380 | bool wait_activity(const QString& name, int timeout = 100 * 1000) 381 | { 382 | auto t = system_clock::now(); 383 | do { 384 | if (activity() == name) return true; 385 | Sleep(100); 386 | } while (duration_cast(system_clock::now() - t).count() < timeout); 387 | return false; 388 | } 389 | 390 | // 某个APP是否处于活动状态 391 | bool app_active(const QString& name) 392 | { 393 | return adb_shell(QStringList {"dumpsys", "window", "|", "grep", name}).size() > 0; 394 | } 395 | 396 | QString name; // 设备名称 397 | }; 398 | 399 | #endif // __ADBDEVICE_H__ 400 | -------------------------------------------------------------------------------- /QtAdb/QtAdb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ui_QtAdb.h" 10 | 11 | #include "AdbDevice.h" 12 | #include "PsDlg.h" 13 | 14 | using namespace std; 15 | 16 | class DeviceComboBox : public QComboBox 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | DeviceComboBox(QWidget *p): QComboBox(p) 22 | { 23 | connect(this, 24 | static_cast(&QComboBox::currentIndexChanged), 25 | this, &DeviceComboBox::onChanged); 26 | } 27 | 28 | void onChanged(int i) 29 | { 30 | emit deviceChanged((AdbDevice*)itemData(i).value()); 31 | } 32 | 33 | // 更新设备列表 34 | void updateDevices() 35 | { 36 | auto oldDevs = devs; 37 | auto set = QSet::fromList(AdbDevice::devices()); 38 | // 更新已有列表 39 | for (int i = 0; i < count(); ++i) 40 | { 41 | auto devName = itemText(i).split(QRegExp("\\s+"))[0]; 42 | auto dev = (AdbDevice*)itemData(i).value(); 43 | if (set.remove(devName)) 44 | { 45 | // 手机型号 46 | setItemText(i, devName + "\t" + dev->model()); 47 | } 48 | else 49 | { 50 | // 列表里不存在的删掉 51 | removeItem(i); 52 | delete dev; 53 | } 54 | } 55 | // 添加新的设备 56 | for (auto devName : set) 57 | { 58 | auto dev = new AdbDevice(devName); 59 | // 手机型号 60 | addItem(devName + "\t" + dev->model(), (quintptr)dev); 61 | } 62 | } 63 | 64 | protected: 65 | void showPopup() 66 | { 67 | updateDevices(); 68 | QComboBox::showPopup(); 69 | } 70 | 71 | Q_SIGNALS: 72 | void deviceChanged(AdbDevice*); 73 | 74 | private: 75 | QHash devs; 76 | }; 77 | 78 | class TableFilter: public QLineEdit 79 | { 80 | Q_OBJECT 81 | 82 | public: 83 | static void install(QWidget *parent) 84 | { 85 | auto t = (TableFilter*)parent->property("filter").value(); 86 | if (!t) parent->setProperty("filter", (quintptr)(new TableFilter(parent))); 87 | } 88 | 89 | static void display(QObject *parent) 90 | { 91 | auto t = (TableFilter*)parent->property("filter").value(); 92 | if (t) t->display(); 93 | } 94 | 95 | private: 96 | int timer = 0; 97 | 98 | TableFilter(QWidget *parent): QLineEdit(parent) 99 | { 100 | setPlaceholderText("过滤"); 101 | parent->installEventFilter(this); 102 | this->hide(); 103 | connect(this, &QLineEdit::textChanged, this, &TableFilter::onChanged); 104 | auto pl = palette(); 105 | pl.setColor(QPalette::Background, QColor(0, 0, 0, 100)); 106 | setPalette(pl); 107 | } 108 | 109 | void display() 110 | { 111 | const int padding = 1; 112 | const int height = 30; 113 | auto p = (QWidget*)parent(); 114 | auto& g = p->geometry(); 115 | setGeometry(padding, g.height() - height, g.width() - padding, height); 116 | //raise(); p->stackUnder(topLevelWidget()); 117 | show(); setFocus(); 118 | } 119 | 120 | void doFilter(QTreeWidget *p) 121 | { 122 | if (!p) return; 123 | QSet set; // 匹配的行 124 | for (auto i : p->findItems(text(), Qt::MatchContains)) 125 | set.insert(i); 126 | QTreeWidgetItemIterator it(p); 127 | while (*it) 128 | { 129 | auto i = *it++; 130 | i->setHidden(!set.contains(i)); 131 | } 132 | } 133 | 134 | void doFilter(QTableWidget *p) 135 | { 136 | if (!p) return; 137 | QSet set; // 匹配的行 138 | for (auto i : p->findItems(text(), Qt::MatchContains)) 139 | set.insert(i->row()); 140 | for (int i = 0; i < p->rowCount(); i++) 141 | p->setRowHidden(i, !set.contains(i)); 142 | } 143 | 144 | void doFilter() 145 | { 146 | doFilter(qobject_cast(parent())); 147 | doFilter(qobject_cast(parent())); 148 | } 149 | 150 | public slots: 151 | void onChanged(const QString& text) 152 | { 153 | if (timer) killTimer(timer); 154 | timer = startTimer(500); 155 | } 156 | 157 | protected: 158 | // 超过一定时间不按键,进行过滤 159 | void timerEvent(QTimerEvent *event) 160 | { 161 | killTimer(timer); 162 | doFilter(); 163 | } 164 | 165 | void keyPressEvent(QKeyEvent *e) 166 | { 167 | if (e->key() == Qt::Key_Return) 168 | return doFilter(); 169 | if (e->key() == Qt::Key_Escape) 170 | return hide(); 171 | QLineEdit::keyPressEvent(e); 172 | } 173 | 174 | // 为父窗口绑定Ctrl+F快捷键 175 | bool eventFilter(QObject *target, QEvent *event) 176 | { 177 | if (target == parent() && event->type() == QEvent::KeyPress) 178 | { 179 | QKeyEvent *e = static_cast(event); 180 | if (e->key() == Qt::Key_F && e->modifiers() == Qt::ControlModifier) 181 | { 182 | display(target); 183 | return true; 184 | } 185 | } 186 | return target->eventFilter(target, event); 187 | } 188 | }; 189 | 190 | class QtAdb : public QMainWindow 191 | { 192 | Q_OBJECT 193 | 194 | public: 195 | QtAdb(QWidget *parent = Q_NULLPTR); 196 | 197 | void log(const QString& text) 198 | { 199 | ui.logEdit->appendPlainText(text); 200 | } 201 | 202 | //void log(QString text) { ui.logEdit->appendPlainText(text); } 203 | 204 | void log(const QStringList& list) 205 | { 206 | for (auto& s : list) log(s); 207 | } 208 | 209 | // 打印详细信息 210 | void logBasicInfo() 211 | { 212 | if (!checkDevice()) return; 213 | 214 | log("[型号]"); 215 | log(cd->shell({ "getprop", "ro.product.model" })); 216 | log("[安卓版本]"); 217 | log(cd->shell({ "getprop", "ro.build.version.release" }) + cd->shell({ "getprop", "ro.product.name" })); 218 | log("[分辨率]"); 219 | log(cd->shell({ "wm", "size" })); 220 | } 221 | 222 | QString storageSize(float size) 223 | { 224 | const char *units[] = { "KB", "MB", "GB", "TB", nullptr }; 225 | int i = -1; 226 | while (size > 1024) size /= 1024, ++i; 227 | auto result = QString::number(size, 'g', 2); 228 | if (i >= 0) result.append(' ').append(units[i]); 229 | return result; 230 | } 231 | 232 | void updatePsTree() 233 | { 234 | if (!checkDevice()) return; 235 | 236 | bool first = ui.treePs->topLevelItemCount() == 0; 237 | for (auto line : cd->shell({ "ps", "-A", "-o", "NAME,PID,PPID,USER,VSZ,CMDLINE", "|", "tail", "-n", "+2"})) 238 | { 239 | LineParser p(std::move(line)); 240 | auto name = p.psname(); 241 | // 忽略线程 242 | if (name[0] == '[') continue; 243 | 244 | auto pid = p.next(); 245 | auto ppid = p.next().toInt(); 246 | auto user = p.next(); 247 | auto mem = p.next(); 248 | auto path = p.rest(); 249 | 250 | auto pid_ = pid.toInt(); 251 | auto item = psMap[pid_]; 252 | if (!item) item = new QTreeWidgetItem(); 253 | 254 | item->setText(0, name); 255 | item->setText(1, pid); 256 | item->setText(2, user); 257 | item->setText(3, storageSize(mem.toFloat())); 258 | item->setText(4, path); 259 | 260 | auto parent = psMap[ppid]; 261 | if (parent) 262 | parent->addChild(item); 263 | else if (first) 264 | ui.treePs->addTopLevelItem(item); 265 | psMap.insert(pid_, item); 266 | } 267 | 268 | if (first) ui.treePs->expandAll(); 269 | } 270 | 271 | public slots: 272 | void changeDevice(AdbDevice *dev) 273 | { 274 | cd = dev; 275 | logBasicInfo(); 276 | onTabChanged(ui.tabWidget->currentIndex()); 277 | } 278 | 279 | bool checkDevice() 280 | { 281 | if (cd) return true; 282 | QSystemTrayIcon tray(this); 283 | tray.showMessage("", "没有设备"); 284 | return false; 285 | } 286 | 287 | void updateAppList() 288 | { 289 | if (!checkDevice()) return; 290 | 291 | auto data = cd->shell({ "pm", "list", "package", "-f" }).split(); 292 | QRegExp reg(R"(package:(.+)=([^=]+)$)"); 293 | ui.appList->setRowCount(data.size()); 294 | for (int i = 0; i < data.size(); ++i) 295 | { 296 | if (data[i].indexOf(reg) < 0) 297 | { 298 | ui.appList->removeRow(i); 299 | log("[warn] " + data[i]); 300 | continue; 301 | } 302 | 303 | auto path = reg.cap(1); 304 | auto package = reg.cap(2); 305 | ui.appList->setItem(i, 0, new QTableWidgetItem(package)); 306 | ui.appList->setItem(i, 1, new QTableWidgetItem(path)); 307 | } 308 | } 309 | 310 | void onTabChanged(int i) 311 | { 312 | auto label = ui.tabWidget->tabText(i); 313 | if (label == "APP") 314 | updateAppList(); 315 | if (label == "进程") 316 | updatePsTree(); 317 | if (label == "文件") 318 | { 319 | if (!ui.treeFs->topLevelItemCount()) updateDirs("/"); 320 | } 321 | } 322 | 323 | void uninstall() 324 | { 325 | // 当前焦点 326 | auto app = ui.appList->item(ui.appList->currentRow(), 0)->text(); 327 | if (QMessageBox::information(this, "卸载应用", app, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) 328 | { 329 | auto r = cd->shell({ "pm", "uninstall", "--user", "0", app }); 330 | log("Uninstall " + app + " :" + r); 331 | updateAppList(); 332 | } 333 | } 334 | 335 | void clearAppSelection() 336 | { 337 | ui.appList->clearSelection(); 338 | } 339 | 340 | void startApp() 341 | { 342 | if (!checkDevice()) return; 343 | 344 | auto app = ui.appList->item(ui.appList->currentRow(), 0)->text(); 345 | auto act = cd->shell({ "dumpsys package " + app + " | awk '/android.intent.action.MAIN:/ { getline; print $2 }'" }).split(); 346 | if (act.size()) log(cd->shell({ "am", "start", act[0] })); 347 | } 348 | 349 | // 输入文本 350 | void inputText() 351 | { 352 | if (!checkDevice()) return; 353 | cd->shell({ "input", "text", ui.lineInputText->text() }); 354 | } 355 | 356 | void execActionCommand() 357 | { 358 | if (!checkDevice()) return; 359 | auto app = ui.appList->item(ui.appList->currentRow(), 0)->text(); 360 | auto a = (QAction*)sender(); 361 | log("[" + a->text() + ": " + app + "]"); 362 | log(cd->shell({ a->toolTip(), app})); 363 | } 364 | 365 | void execShellCommand(QString cmd) 366 | { 367 | if (!checkDevice()) return; 368 | 369 | log("$ " + cmd); 370 | log(cd->shell({ cmd })); 371 | } 372 | 373 | void onCommandDblClicked(QTableWidgetItem *item) 374 | { 375 | execShellCommand(item->text()); 376 | } 377 | 378 | ShellResult ls(const QString& dir) 379 | { 380 | return cd->shell({ "ls -lh " + dir + " | awk 'NR>1{sub(/, +/,\",\");print}'" }); 381 | } 382 | 383 | static QStringList getPath(QTreeWidgetItem *item); 384 | 385 | void onFileExpanded(QTreeWidgetItem *item) 386 | { 387 | QStringList path = getPath(item); 388 | path.front() = ""; 389 | path.push_back(""); 390 | 391 | if (0 == item->childCount()) 392 | { 393 | updateDirs(path.join("/"), item); 394 | } 395 | } 396 | 397 | void onFileItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *old) 398 | { 399 | onFileExpanded(item); 400 | } 401 | 402 | void updateDirs(const QString& dir, QTreeWidgetItem *parent = nullptr) 403 | { 404 | if (!checkDevice()) return; 405 | 406 | auto style = QApplication::style(); 407 | if (!parent) 408 | { 409 | parent = new QTreeWidgetItem(); 410 | parent->setText(0, "/"); 411 | parent->setIcon(0, style->standardIcon(QStyle::SP_DirIcon)); 412 | parent->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 413 | ui.treeFs->addTopLevelItem(parent); 414 | return updateDirs("/", parent); 415 | } 416 | 417 | QRegExp deli("\\s+"); 418 | auto data = ls(dir).split(); 419 | ui.tableFs->setRowCount(data.size()); 420 | for (int i = 0; data.size(); ++i) 421 | { 422 | LineParser p(std::move(data.takeFirst())); 423 | 424 | auto flags = p.next(); 425 | p.next(); 426 | auto user = p.next(); 427 | auto group = p.next(); 428 | auto size = p.next(); 429 | p.next(); 430 | p.next(); 431 | auto name = p.next(); 432 | p.next(); 433 | auto link = p.next(); 434 | 435 | auto file = new QTableWidgetItem(name); 436 | ui.tableFs->setItem(i, 0, file); 437 | ui.tableFs->setItem(i, 1, new QTableWidgetItem(flags)); 438 | ui.tableFs->setItem(i, 2, new QTableWidgetItem(user)); 439 | ui.tableFs->setItem(i, 3, new QTableWidgetItem(group)); 440 | ui.tableFs->setItem(i, 4, new QTableWidgetItem(size)); 441 | 442 | auto item = new QTreeWidgetItem(); 443 | item->setText(0, name); 444 | 445 | bool isDir = false; 446 | bool isLink = false; 447 | if (flags[0] == 'l' && link.size()) 448 | isLink = true, isDir = size == "11"; 449 | else if (flags[0] == 'd') isDir = true; 450 | 451 | ui.tableFs->setItem(i, 4, new QTableWidgetItem(size)); 452 | ui.tableFs->setItem(i, 5, new QTableWidgetItem(isLink ? link: "")); 453 | if (isDir) 454 | { 455 | if (parent) parent->addChild(item); 456 | auto icon = style->standardIcon(isLink ? QStyle::SP_DirLinkIcon : QStyle::SP_DirIcon); 457 | file->setIcon(icon), item->setIcon(0, icon); 458 | item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); 459 | } 460 | else 461 | { 462 | auto icon = style->standardIcon(isLink ? QStyle::SP_FileLinkIcon : QStyle::SP_FileIcon); 463 | file->setIcon(icon), item->setIcon(0, icon); 464 | } 465 | } 466 | parent->setExpanded(true); 467 | } 468 | 469 | void onPsTreeItemDoubleClicked(QTreeWidgetItem *item) 470 | { 471 | auto dlg = new PsDlg(this, cd, item->text(1).toInt()); 472 | dlg->setModal(true); 473 | dlg->show(); 474 | } 475 | 476 | private: 477 | Ui::QtAdbClass ui; 478 | 479 | DeviceComboBox *comboDevice; 480 | QHash psMap; 481 | AdbDevice *cd = nullptr; 482 | QActionGroup *devGroup = new QActionGroup(this); 483 | }; 484 | -------------------------------------------------------------------------------- /QtAdb/QtAdb.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | QtAdbClass 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1572 10 | 1245 11 | 12 | 13 | 14 | QtAdb 15 | 16 | 17 | 18 | 19 | 20 | 21 | Qt::Vertical 22 | 23 | 24 | 25 | Qt::Horizontal 26 | 27 | 28 | 29 | 30 | 0 31 | 0 32 | 33 | 34 | 35 | 常用功能 36 | 37 | 38 | 39 | 40 | 41 | 0 42 | 43 | 44 | QLayout::SetDefaultConstraint 45 | 46 | 47 | 48 | 49 | 50 | 0 51 | 0 52 | 53 | 54 | 55 | 当前设备(&D): 56 | 57 | 58 | comboDevice 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 0 67 | 0 68 | 69 | 70 | 71 | 72 | 200 73 | 0 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 0 83 | 0 84 | 85 | 86 | 87 | 模拟输入(&I): 88 | 89 | 90 | lineInputText 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 0 99 | 0 100 | 101 | 102 | 103 | 输入文本 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 0 112 | 0 113 | 114 | 115 | 116 | 截图(&C) 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 0 127 | 0 128 | 129 | 130 | 131 | selection-background-color: rgb(204, 232, 255); 132 | background-color: rgb(255, 255, 255); 133 | selection-color: rgb(0, 0, 0); 134 | 135 | 136 | QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed 137 | 138 | 139 | QAbstractItemView::SingleSelection 140 | 141 | 142 | QAbstractItemView::SelectRows 143 | 144 | 145 | false 146 | 147 | 148 | Qt::SolidLine 149 | 150 | 151 | false 152 | 153 | 154 | true 155 | 156 | 157 | true 158 | 159 | 160 | false 161 | 162 | 163 | 25 164 | 165 | 166 | 25 167 | 168 | 169 | 170 | 拨打电话 171 | 172 | 173 | 174 | 175 | 发送短信 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 当前Activity 206 | 207 | 208 | 209 | 210 | 列举安卓用户 211 | 212 | 213 | 214 | 215 | 命令(双击执行,F2修改) 216 | 217 | 218 | 219 | 220 | am start -a android.intent.action.CALL -d tel:10000 221 | 222 | 223 | 224 | 225 | am start -a android.intent.action.SENDTO -d sms:10086 --es sms_body hello 226 | 227 | 228 | 229 | 230 | service list 231 | 232 | 233 | 234 | 235 | dumpsys statusbar 236 | 237 | 238 | 239 | 240 | dumpsys cpuinfo 241 | 242 | 243 | 244 | 245 | dumpsys activity top 246 | 247 | 248 | 249 | 250 | am kill-all 251 | 252 | 253 | 254 | 255 | dumpsys window w | grep name= | awk '/name=StatusBar/{getline; print}' 256 | 257 | 258 | 259 | 260 | pm list users 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 1 270 | 271 | 272 | 273 | APP 274 | 275 | 276 | 277 | 278 | 279 | Qt::ActionsContextMenu 280 | 281 | 282 | selection-background-color: rgb(204, 232, 255); 283 | selection-color: rgb(0, 0, 0); 284 | 285 | 286 | QFrame::StyledPanel 287 | 288 | 289 | QFrame::Sunken 290 | 291 | 292 | QAbstractItemView::NoEditTriggers 293 | 294 | 295 | QAbstractItemView::SingleSelection 296 | 297 | 298 | QAbstractItemView::SelectRows 299 | 300 | 301 | false 302 | 303 | 304 | true 305 | 306 | 307 | true 308 | 309 | 310 | 10 311 | 312 | 313 | 100 314 | 315 | 316 | true 317 | 318 | 319 | true 320 | 321 | 322 | false 323 | 324 | 325 | 20 326 | 327 | 328 | 20 329 | 330 | 331 | false 332 | 333 | 334 | 335 | 名称 336 | 337 | 338 | 339 | 340 | 路径 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 文件 350 | 351 | 352 | 353 | 354 | 355 | Qt::Horizontal 356 | 357 | 358 | 359 | 15 360 | 361 | 362 | false 363 | 364 | 365 | false 366 | 367 | 368 | 369 | 文件 370 | 371 | 372 | 373 | 374 | 375 | selection-background-color: rgb(204, 232, 255); 376 | selection-color: rgb(0, 0, 0); 377 | 378 | 379 | QAbstractItemView::SingleSelection 380 | 381 | 382 | QAbstractItemView::SelectRows 383 | 384 | 385 | false 386 | 387 | 388 | true 389 | 390 | 391 | false 392 | 393 | 394 | 20 395 | 396 | 397 | 20 398 | 399 | 400 | 401 | 文件 402 | 403 | 404 | 405 | 406 | 权限 407 | 408 | 409 | 410 | 411 | 用户 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 大小 422 | 423 | 424 | 425 | 426 | 链接 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 进程 437 | 438 | 439 | 440 | 441 | 442 | 15 443 | 444 | 445 | false 446 | 447 | 448 | 449 | 进程 450 | 451 | 452 | 453 | 454 | PID 455 | 456 | 457 | 458 | 459 | 用户 460 | 461 | 462 | 463 | 464 | 内存 465 | 466 | 467 | 468 | 469 | 命令行 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 日志 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 卸载 495 | 496 | 497 | 498 | 499 | 清除选择 500 | 501 | 502 | 清除 503 | 504 | 505 | 506 | 507 | 刷新列表 508 | 509 | 510 | 刷新列表 511 | 512 | 513 | 514 | 515 | 查看服务 516 | 517 | 518 | dumpsys activity s 519 | 520 | 521 | 522 | 523 | 查看包信息 524 | 525 | 526 | dumpsys package 527 | 528 | 529 | 530 | 531 | 启动 532 | 533 | 534 | 启动应用 535 | 536 | 537 | 538 | 539 | 停止运行 540 | 541 | 542 | am force-stop 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | actionUninstall 553 | triggered() 554 | QtAdbClass 555 | uninstall() 556 | 557 | 558 | -1 559 | -1 560 | 561 | 562 | 674 563 | 483 564 | 565 | 566 | 567 | 568 | actionClearSelect 569 | triggered() 570 | QtAdbClass 571 | clearAppSelection() 572 | 573 | 574 | -1 575 | -1 576 | 577 | 578 | 674 579 | 483 580 | 581 | 582 | 583 | 584 | lineInputText 585 | returnPressed() 586 | QtAdbClass 587 | inputText() 588 | 589 | 590 | 769 591 | 64 592 | 593 | 594 | 418 595 | 0 596 | 597 | 598 | 599 | 600 | actionUpdateAppList 601 | triggered() 602 | QtAdbClass 603 | updateAppList() 604 | 605 | 606 | -1 607 | -1 608 | 609 | 610 | 674 611 | 483 612 | 613 | 614 | 615 | 616 | actionDumpService 617 | triggered() 618 | QtAdbClass 619 | execActionCommand() 620 | 621 | 622 | -1 623 | -1 624 | 625 | 626 | 674 627 | 512 628 | 629 | 630 | 631 | 632 | actionDumpPackage 633 | triggered() 634 | QtAdbClass 635 | execActionCommand() 636 | 637 | 638 | -1 639 | -1 640 | 641 | 642 | 674 643 | 512 644 | 645 | 646 | 647 | 648 | actionStart 649 | triggered() 650 | QtAdbClass 651 | startApp() 652 | 653 | 654 | -1 655 | -1 656 | 657 | 658 | 697 659 | 510 660 | 661 | 662 | 663 | 664 | tableWidget 665 | itemDoubleClicked(QTableWidgetItem*) 666 | QtAdbClass 667 | onCommandDblClicked(QTableWidgetItem*) 668 | 669 | 670 | 856 671 | 347 672 | 673 | 674 | 614 675 | 0 676 | 677 | 678 | 679 | 680 | actionStop 681 | triggered() 682 | QtAdbClass 683 | execActionCommand() 684 | 685 | 686 | -1 687 | -1 688 | 689 | 690 | 697 691 | 510 692 | 693 | 694 | 695 | 696 | appList 697 | pressed(QModelIndex) 698 | QtAdbClass 699 | onTablePressed(QModelIndex) 700 | 701 | 702 | 972 703 | 53 704 | 705 | 706 | 917 707 | 0 708 | 709 | 710 | 711 | 712 | treeFs 713 | expanded(QModelIndex) 714 | QtAdbClass 715 | onFileExpanded(QModelIndex) 716 | 717 | 718 | 1217 719 | 409 720 | 721 | 722 | 721 723 | 0 724 | 725 | 726 | 727 | 728 | tabWidget 729 | currentChanged(int) 730 | QtAdbClass 731 | onTabChanged(int) 732 | 733 | 734 | 1139 735 | 74 736 | 737 | 738 | 394 739 | 0 740 | 741 | 742 | 743 | 744 | treeFs 745 | itemExpanded(QTreeWidgetItem*) 746 | QtAdbClass 747 | onFileExpanded(QTreeWidgetItem*) 748 | 749 | 750 | 1217 751 | 275 752 | 753 | 754 | 593 755 | 0 756 | 757 | 758 | 759 | 760 | treePs 761 | doubleClicked(QModelIndex) 762 | QtAdbClass 763 | onPsTreeDblClicked(QModelIndex) 764 | 765 | 766 | 1154 767 | 288 768 | 769 | 770 | 884 771 | -17 772 | 773 | 774 | 775 | 776 | treePs 777 | itemDoubleClicked(QTreeWidgetItem*,int) 778 | QtAdbClass 779 | onPsTreeItemDoubleClicked(QTreeWidgetItem*) 780 | 781 | 782 | 1303 783 | 246 784 | 785 | 786 | 1207 787 | -16 788 | 789 | 790 | 791 | 792 | treeFs 793 | currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*) 794 | QtAdbClass 795 | onFileItemChanged(QTreeWidgetItem*,QTreeWidgetItem*) 796 | 797 | 798 | 1059 799 | 249 800 | 801 | 802 | 1177 803 | -69 804 | 805 | 806 | 807 | 808 | 809 | onTabChanged(int) 810 | uninstall() 811 | clearAppSelection() 812 | inputText() 813 | updateAppList() 814 | execShellCommand(QString) 815 | execActionCommand() 816 | startApp() 817 | onCommandDblClicked(QTableWidgetItem*) 818 | onFileExpanded(QTreeWidgetItem*) 819 | onTablePressed(QModelIndex) 820 | onPsTreeItemDoubleClicked(QTreeWidgetItem*) 821 | onFileItemChanged(QTreeWidgetItem*,QTreeWidgetItem*) 822 | 823 | 824 | --------------------------------------------------------------------------------