├── .gitignore
├── .travis.yml
├── Dependency-Injection-Graph-mapping.txt
├── LICENSE
├── README.md
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── proguard.txt
└── src
└── main
├── java
├── actions
│ ├── GenerateDependencyInjectionGraph.java
│ ├── SetApkPathAction.java
│ ├── SetInnerClassMode.java
│ ├── SetPackageFilter.java
│ └── ShowGeneratedDependencies.java
├── managers
│ ├── FileChooserDialogManager.java
│ └── PropertiesManager.java
└── utils
│ ├── CustomBaksmali.java
│ ├── FileTypes.java
│ ├── PathHelper.java
│ ├── PropertyKeys.java
│ ├── SmaliAnalyzer.java
│ ├── Strings.java
│ └── Writer.java
└── resources
├── META-INF
└── plugin.xml
└── web
├── index.html
└── scripts
├── dependency.css
├── graph-actions-select-compiled.js
├── graph-actions-select.js
├── parse-compiled.js
└── parse.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Mac
2 | .DS_Store
3 |
4 | /.idea
5 | /build
6 | .gradle/
7 | gradle/
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | install: true
3 |
4 | jdk:
5 | - oraclejdk11
6 |
7 | script:
8 | - ./gradlew build --stacktrace
9 | - ./gradlew verifyPlugin --stacktrace
10 | - ./gradlew buildPlugin --stacktrace
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2017 Necati Caner Gaygisiz
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # APK Dependency Graph
2 | [](https://travis-ci.org/kaygisiz/Dependency-Injection-Graph)
3 |
4 | APK Dependency Graph is a plugin for Android Studio that allows display graph of dependencies.
5 |
6 | ## How to install
7 | - in Android Studio: go to `Settings → Plugins → Browse repositories` and search for `APK Dependency Graph`
8 |
9 | _or_
10 |
11 | - [download it](http://plugins.jetbrains.com/plugin/10107) and install via `Preferences → Plugins → Install plugin from disk`
12 |
13 | ## How to use
14 |
15 | 1. Go to `Settings → Instan Run` in Android Studio and **disable** `Enable Instant Run to hot swap code/resource changes on deploy`
16 | 2. Build Apk with disabled Instant Run
17 | 3. Click `Tools → APK Dependency Graph → Generate Dependency Graph`
18 | 4. Select apk file from shown dialog
19 | 5. Click `Tools → APK Dependency Graph → Set Package Filter` enter `package name` (e.g. **com.example.package**) as a filter so you will avoid unnecessary dependencies in your graph.
20 |
21 | ### Optional
22 |
23 | - Go to `Tools → APK Dependency Graph → Show Generated Graph` to see last generated dependencies graph of current project in browser.
24 | - Select `Tools → APK Dependency Graph → Disable Inner Classes` to define if you want to skip inner classes on your graph or not.
25 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 | dependencies {
6 | classpath 'net.sf.proguard:proguard-gradle:6.2.2'
7 | }
8 | }
9 |
10 | import proguard.gradle.ProGuardTask
11 |
12 | plugins {
13 | id 'java-library'
14 | id 'org.jetbrains.intellij' version '0.4.21'
15 | }
16 |
17 | version '2.3'
18 |
19 | java {
20 | sourceCompatibility = JavaVersion.VERSION_1_8
21 | targetCompatibility = JavaVersion.VERSION_1_8
22 | }
23 |
24 | repositories {
25 | mavenCentral()
26 | }
27 |
28 | dependencies {
29 | compile group: 'org.smali', name: 'baksmali', version: '2.4.0'
30 | compile group: 'org.smali', name: 'dexlib2', version: '2.4.0'
31 | compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.10'
32 | }
33 |
34 | def jarFileName = "${project.name}-${project.version}"
35 | def publishToken = hasProperty('intellijPublishToken') ? intellijPublishToken : 'fake_token_for_CI'
36 |
37 | // See https://github.com/JetBrains/gradle-intellij-plugin/
38 |
39 | patchPluginXml {
40 | version version
41 | sinceBuild '141'
42 | changeNotes """
43 |
v2.3
44 |
Enhancements
45 |
46 |
Reduce plugin size.
47 |
Compatible with Android Studio 4.0.
48 |
Update plugin name.
49 |
50 | """
51 | }
52 |
53 | publishPlugin {
54 | token publishToken
55 | // channels 'beta'
56 | }
57 |
58 | runIde {
59 | ideDirectory '/Applications/Android Studio.app/Contents'
60 | }
61 |
62 | task copyWebResources(type: Copy) {
63 | from('src/main/resources') {
64 | into 'resources'
65 | }
66 | into "${buildDir}/idea-sandbox/plugins/${project.name}/lib"
67 | exclude 'META-INF'
68 | }
69 |
70 | prepareSandbox.finalizedBy(copyWebResources)
71 | prepareSandbox.doLast {
72 | delete fileTree("${buildDir}/idea-sandbox/plugins/${project.name}/lib") {
73 | exclude "${jarFileName}.jar"
74 | }
75 | }
76 |
77 | buildSearchableOptions.enabled = false
78 | jarSearchableOptions.enabled = false
79 |
80 | jar {
81 | archiveFileName = "${jarFileName}.jar"
82 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
83 | }
84 |
85 | def injarPath = "${buildDir}/libs/${jarFileName}.jar"
86 | def outjarPath = "${buildDir}/proguard/${jarFileName}.jar"
87 | task proguard(type: ProGuardTask, dependsOn: jar) {
88 | configuration 'proguard.txt'
89 | printmapping "${project.name}-mapping.txt"
90 |
91 | injars injarPath
92 | outjars outjarPath
93 |
94 | def rtPath = 'jre/lib/rt.jar'
95 | if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
96 | rtPath = 'jmods'
97 | }
98 | libraryjars "${System.getProperty('java.home')}/${rtPath}"
99 | libraryjars project.configurations.compile
100 |
101 | doLast {
102 | delete file(injarPath)
103 | ant.move(file: outjarPath, tofile: injarPath)
104 | }
105 | }
106 |
107 | jar.finalizedBy(proguard)
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kaygisiz/APK-Dependency-Graph-Plugin/17b504afb5559d7b4447be22b7886c34751c6a08/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/proguard.txt:
--------------------------------------------------------------------------------
1 | -renamesourcefileattribute SourceFile
2 | -dontwarn com.intellij.**
3 |
4 | -keep class actions.** { *; }
5 | -keep class managers.** { *; }
6 | -keep class utils.** { *; }
7 |
8 | -keep class javax.** { *; }
9 | -keep class java.** { *; }
10 |
11 | -keep class org.jf.dexlib2.dexbacked.DexBackedDexFile { *; }
12 | -keep class org.jf.dexlib2.iface.DexFile { *; }
13 | -keep class org.jf.dexlib2.dexbacked.DexBackedDexFile { *; }
14 | -keep class org.jf.dexlib2.DexFileFactory { *; }
15 | -keep class org.jf.dexlib2.Opcodes { *; }
16 | -keep class org.jf.dexlib2.Opcode { *; }
17 |
18 | -keep class com.google.common.collect.Ordering { *; }
19 | -keep class org.apache.commons.lang3.StringUtils {
20 | boolean isNumeric(java.lang.CharSequence);
21 | boolean isEmpty(java.lang.CharSequence);
22 | }
--------------------------------------------------------------------------------
/src/main/java/actions/GenerateDependencyInjectionGraph.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package actions;
17 |
18 | import com.intellij.openapi.actionSystem.AnAction;
19 | import com.intellij.openapi.actionSystem.AnActionEvent;
20 | import com.intellij.openapi.project.Project;
21 | import com.intellij.openapi.vfs.LocalFileSystem;
22 | import com.intellij.openapi.vfs.VirtualFile;
23 | import managers.FileChooserDialogManager;
24 | import managers.PropertiesManager;
25 | import org.apache.commons.lang3.StringUtils;
26 | import utils.FileTypes;
27 | import utils.PropertyKeys;
28 | import utils.Strings;
29 |
30 | public class SetApkPathAction extends AnAction {
31 | @Override
32 | public void actionPerformed(AnActionEvent anActionEvent) {
33 | Project project = anActionEvent.getProject();
34 | if (project != null) {
35 | String currentApkPath = PropertiesManager.getData(project, PropertyKeys.APK_PATH);
36 |
37 | VirtualFile fileToSelectOnCreate =
38 | StringUtils.isEmpty(currentApkPath)
39 | ? project.getBaseDir()
40 | : LocalFileSystem.getInstance().findFileByPath(currentApkPath);
41 |
42 | VirtualFile apkFile = new FileChooserDialogManager.Builder(project, fileToSelectOnCreate)
43 | .setFileTypes(FileTypes.FILE)
44 | .setTitle(Strings.TITLE_ASK_APK_FILE)
45 | .setDescription(Strings.MESSAGE_ASK_APK_FILE)
46 | .withFileFilter("apk")
47 | .create()
48 | .getSelectedFile();
49 |
50 | if (apkFile != null) {
51 | PropertiesManager.putData(project, PropertyKeys.APK_PATH, apkFile.getPath());
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/actions/SetInnerClassMode.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package actions;
17 |
18 | import com.intellij.openapi.actionSystem.AnActionEvent;
19 | import com.intellij.openapi.actionSystem.ToggleAction;
20 | import com.intellij.openapi.project.Project;
21 | import managers.PropertiesManager;
22 | import utils.PropertyKeys;
23 | import utils.Strings;
24 |
25 | public class SetInnerClassMode extends ToggleAction {
26 | @Override
27 | public boolean isSelected(AnActionEvent anActionEvent) {
28 | return anActionEvent.getProject() != null
29 | && PropertiesManager.getData(anActionEvent.getProject(), PropertyKeys.IS_INNER_CLASS_ENABLE, Strings.TRUE).equals(Strings.TRUE);
30 | }
31 |
32 | @Override
33 | public void setSelected(AnActionEvent anActionEvent, boolean b) {
34 | Project project = anActionEvent.getProject();
35 | if (project != null) {
36 | PropertiesManager.putData(project, PropertyKeys.IS_INNER_CLASS_ENABLE, b ? Strings.TRUE : Strings.FALSE);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/actions/SetPackageFilter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package actions;
17 |
18 | import com.intellij.openapi.actionSystem.AnAction;
19 | import com.intellij.openapi.actionSystem.AnActionEvent;
20 | import com.intellij.openapi.project.Project;
21 | import com.intellij.openapi.ui.Messages;
22 | import com.intellij.openapi.ui.NonEmptyInputValidator;
23 | import managers.PropertiesManager;
24 | import org.apache.commons.lang3.StringUtils;
25 | import utils.PropertyKeys;
26 | import utils.Strings;
27 |
28 | public class SetPackageFilter extends AnAction {
29 | @Override
30 | public void actionPerformed(AnActionEvent anActionEvent) {
31 | Project project = anActionEvent.getProject();
32 |
33 | if (project != null) {
34 |
35 | String packageName = Messages.showInputDialog(
36 | Strings.MESSAGE_ASK_PACKAGE_NAME_TO_FILTER,
37 | Strings.TITLE_ASK_PACKAGE_NAME_TO_FILTER,
38 | Messages.getQuestionIcon(),
39 | PropertiesManager.getData(project, PropertyKeys.PACKAGE_NAME),
40 | new NonEmptyInputValidator());
41 |
42 | if (!StringUtils.isEmpty(packageName)) {
43 | PropertiesManager.putData(project, PropertyKeys.PACKAGE_NAME, packageName);
44 | }
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/main/java/actions/ShowGeneratedDependencies.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package actions;
17 |
18 | import com.intellij.openapi.actionSystem.AnAction;
19 | import com.intellij.openapi.actionSystem.AnActionEvent;
20 | import com.intellij.openapi.project.Project;
21 | import com.intellij.openapi.ui.Messages;
22 | import utils.PathHelper;
23 | import utils.Strings;
24 |
25 | import java.awt.*;
26 | import java.io.File;
27 | import java.io.IOException;
28 |
29 | public class ShowGeneratedDependencies extends AnAction {
30 |
31 | private PathHelper pathHelper;
32 |
33 | @Override
34 | public void actionPerformed(AnActionEvent anActionEvent) {
35 | Project project = anActionEvent.getProject();
36 | if (project != null) {
37 |
38 | pathHelper = new PathHelper(project);
39 |
40 | if (new File(pathHelper.getAnalyzedJsFile()).exists()) {
41 | openBrowser();
42 | } else {
43 | Messages.showInfoMessage(Strings.ERROR_SHOW_GENERATED_DEPENDENCIES, Strings.TITLE_ERROR_SHOW_GENERATED_DEPENDENCIES);
44 | }
45 |
46 | }
47 | }
48 |
49 | private void openBrowser() {
50 | try {
51 | Desktop.getDesktop().browse(new File(pathHelper.getIndexHtmlFile()).toURI());
52 | } catch (IOException e) {
53 | e.printStackTrace();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/managers/FileChooserDialogManager.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package managers;
17 |
18 | import com.intellij.ide.util.PropertiesComponent;
19 | import com.intellij.openapi.project.Project;
20 |
21 | public class PropertiesManager {
22 |
23 | public static String getData(Project project, String key, String defaultValue) {
24 | return loadFromPropertiesComponent(project, key, defaultValue);
25 | }
26 |
27 | public static String getData(Project project, String key) {
28 | return loadFromPropertiesComponent(project, key, "");
29 | }
30 |
31 | public static String getData(String key, String defaultValue) {
32 | return loadFromPropertiesComponent(null, key, defaultValue);
33 | }
34 |
35 | public static String getData(String key) {
36 | return loadFromPropertiesComponent(null, key, "");
37 | }
38 |
39 | public static void putData(Project project, String key, String value) {
40 | saveToPropertiesComponent(project, key, value);
41 | }
42 |
43 | public static void putData(String key, String value) {
44 | saveToPropertiesComponent(null, key, value);
45 | }
46 |
47 | private static String loadFromPropertiesComponent(Project project, String key, String defaultValue) {
48 | PropertiesComponent propertiesComponent = project == null ? PropertiesComponent.getInstance() : PropertiesComponent.getInstance(project);
49 |
50 | return propertiesComponent.getValue(key, defaultValue);
51 | }
52 |
53 | private static void saveToPropertiesComponent(Project project, String key, String value) {
54 | PropertiesComponent propertiesComponent = project == null ? PropertiesComponent.getInstance() : PropertiesComponent.getInstance(project);
55 |
56 | propertiesComponent.setValue(key, value);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/utils/CustomBaksmali.java:
--------------------------------------------------------------------------------
1 | /*
2 | * [The "BSD licence"]
3 | * Copyright (c) 2010 Ben Gruver (JesusFreke)
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | * 1. Redistributions of source code must retain the above copyright
10 | * notice, this list of conditions and the following disclaimer.
11 | * 2. Redistributions in binary form must reproduce the above copyright
12 | * notice, this list of conditions and the following disclaimer in the
13 | * documentation and/or other materials provided with the distribution.
14 | * 3. The name of the author may not be used to endorse or promote products
15 | * derived from this software without specific prior written permission.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package utils;
30 |
31 | import com.google.common.collect.Ordering;
32 | import org.jf.baksmali.Adaptors.ClassDefinition;
33 | import org.jf.baksmali.BaksmaliOptions;
34 | import org.jf.dexlib2.analysis.InlineMethodResolver;
35 | import org.jf.dexlib2.dexbacked.DexBackedDexFile;
36 | import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
37 | import org.jf.dexlib2.iface.ClassDef;
38 | import org.jf.dexlib2.iface.DexFile;
39 | import org.jf.util.ClassFileNameHandler;
40 | import org.jf.util.IndentingWriter;
41 |
42 | import javax.annotation.Nullable;
43 | import java.io.*;
44 | import java.io.Writer;
45 | import java.util.ArrayList;
46 | import java.util.List;
47 | import java.util.concurrent.*;
48 |
49 | public class CustomBaksmali {
50 | public static boolean disassembleDexFile(DexFile dexFile, File outputDir, @Nullable String packageName) {
51 | final BaksmaliOptions options = getSmaliOptions((DexBackedDexFile) dexFile);
52 | int jobs = getNumberOfAvailableProcessors();
53 | //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
54 | //name collisions, then we'll use the same name for each class, if the dex file goes through multiple
55 | //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames
56 | //may still change of course
57 | List extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
58 |
59 | final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDir, ".smali");
60 |
61 | ExecutorService executor = Executors.newFixedThreadPool(jobs);
62 | List> tasks = new ArrayList<>();
63 |
64 | for (final ClassDef classDef : classDefs) {
65 | if (packageName != null && !classDef.getType().contains(packageName.replaceAll("\\.", "/"))) {
66 | continue;
67 | }
68 | tasks.add(executor.submit(() -> disassembleClass(classDef, fileNameHandler, options)));
69 | }
70 |
71 | boolean errorOccurred = false;
72 | try {
73 | for (Future task : tasks) {
74 | while (true) {
75 | try {
76 | if (!task.get()) {
77 | errorOccurred = true;
78 | }
79 | } catch (InterruptedException ex) {
80 | continue;
81 | } catch (ExecutionException ex) {
82 | throw new RuntimeException(ex);
83 | }
84 | break;
85 | }
86 | }
87 | } finally {
88 | executor.shutdown();
89 | }
90 | return !errorOccurred;
91 | }
92 |
93 | private static boolean disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
94 | BaksmaliOptions options) {
95 | /**
96 | * The path for the disassembly file is based on the package name
97 | * The class descriptor will look something like:
98 | * Ljava/lang/Object;
99 | * Where the there is leading 'L' and a trailing ';', and the parts of the
100 | * package name are separated by '/'
101 | */
102 | String classDescriptor = classDef.getType();
103 |
104 | //validate that the descriptor is formatted like we expect
105 | if (classDescriptor.charAt(0) != 'L' ||
106 | classDescriptor.charAt(classDescriptor.length() - 1) != ';') {
107 | System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class");
108 | return false;
109 | }
110 |
111 | File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
112 |
113 | //create and initialize the top level string template
114 | ClassDefinition classDefinition = new ClassDefinition(options, classDef);
115 |
116 | //write the disassembly
117 | Writer writer = null;
118 | try {
119 | File smaliParent = smaliFile.getParentFile();
120 | if (!smaliParent.exists()) {
121 | if (!smaliParent.mkdirs()) {
122 | // check again, it's likely it was created in a different thread
123 | if (!smaliParent.exists()) {
124 | System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class");
125 | return false;
126 | }
127 | }
128 | }
129 |
130 | if (!smaliFile.exists()) {
131 | if (!smaliFile.createNewFile()) {
132 | System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class");
133 | return false;
134 | }
135 | }
136 |
137 | BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(
138 | new FileOutputStream(smaliFile), "UTF8"));
139 |
140 | writer = new IndentingWriter(bufWriter);
141 | classDefinition.writeTo((IndentingWriter) writer);
142 | } catch (Exception ex) {
143 | System.err.println("\n\nError occurred while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
144 | ex.printStackTrace();
145 | // noinspection ResultOfMethodCallIgnored
146 | smaliFile.delete();
147 | return false;
148 | } finally {
149 | if (writer != null) {
150 | try {
151 | writer.close();
152 | } catch (Throwable ex) {
153 | System.err.println("\n\nError occurred while closing file " + smaliFile.toString());
154 | ex.printStackTrace();
155 | }
156 | }
157 | }
158 | return true;
159 | }
160 |
161 | private static BaksmaliOptions getSmaliOptions(final DexBackedDexFile dexFile) {
162 | final BaksmaliOptions options = new BaksmaliOptions();
163 |
164 | options.deodex = false;
165 | options.implicitReferences = false;
166 | options.parameterRegisters = true;
167 | options.localsDirective = true;
168 | options.sequentialLabels = true;
169 | options.debugInfo = false;
170 | options.codeOffsets = false;
171 | options.accessorComments = false;
172 | options.registerInfo = 0;
173 |
174 | if (dexFile instanceof DexBackedOdexFile) {
175 | options.inlineResolver =
176 | InlineMethodResolver.createInlineMethodResolver(
177 | ((DexBackedOdexFile) dexFile).getOdexVersion());
178 | } else {
179 | options.inlineResolver = null;
180 | }
181 |
182 | return options;
183 | }
184 |
185 | private static int getNumberOfAvailableProcessors() {
186 | int jobs = Runtime.getRuntime().availableProcessors();
187 | return Math.min(jobs, 6);
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/main/java/utils/FileTypes.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package utils;
17 |
18 | import java.lang.annotation.ElementType;
19 | import java.lang.annotation.Retention;
20 | import java.lang.annotation.RetentionPolicy;
21 | import java.lang.annotation.Target;
22 |
23 | @Target(ElementType.PARAMETER)
24 | @Retention(RetentionPolicy.SOURCE)
25 | public @interface FileTypes {
26 | int FILE = 0;
27 | int FOLDER = 1;
28 | int JAR = 2;
29 | int JAR_AS_FILE = 3;
30 | int JAR_CONTENT = 4;
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/utils/PathHelper.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | import com.intellij.openapi.project.Project;
4 |
5 | public class PathHelper {
6 |
7 | private final String digPath;
8 |
9 | private final String decompiledFilesPath;
10 |
11 | private final String webPath;
12 |
13 | private final String analyzedJsPath;
14 |
15 | private final String htmlPath;
16 |
17 | public PathHelper(Project project) {
18 | digPath = replaceCharWithSpace(project.getBasePath()) + "/.dig";
19 | decompiledFilesPath = digPath + "/decompiled";
20 | webPath = digPath + "/web";
21 | analyzedJsPath = webPath + "/analyzed.js";
22 | htmlPath = webPath + "/index.html";
23 | }
24 |
25 | public String getDig() {
26 | return digPath;
27 | }
28 |
29 | public String getDecompiledDir() {
30 | return decompiledFilesPath;
31 | }
32 |
33 | public String getWebDir() {
34 | return webPath;
35 | }
36 |
37 | public String getAnalyzedJsFile() {
38 | return analyzedJsPath;
39 | }
40 |
41 | public String getIndexHtmlFile() {
42 | return htmlPath;
43 | }
44 |
45 | public String replaceCharWithSpace(String path) {
46 | return path.replace("%20", " ");
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/utils/PropertyKeys.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package utils;
17 |
18 | public class PropertyKeys {
19 | public static final String APK_PATH = "APK_PATH";
20 | public static final String PACKAGE_NAME = "PACKAGE_NAME";
21 | public static final String IS_INNER_CLASS_ENABLE = "IS_INNER_CLASS_ENABLE";
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/utils/SmaliAnalyzer.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileNotFoundException;
6 | import java.io.FileReader;
7 | import java.io.IOException;
8 | import java.util.HashMap;
9 | import java.util.HashSet;
10 | import java.util.Map;
11 | import java.util.Set;
12 |
13 | import com.intellij.openapi.project.Project;
14 |
15 | import static org.apache.commons.lang3.StringUtils.isNumeric;
16 |
17 | public class SmaliAnalyzer {
18 |
19 | private Project project;
20 | private boolean isProcessingInner;
21 | private String packageName;
22 |
23 | public SmaliAnalyzer(Project project, boolean isProcessingInner, String packageName) {
24 | this.project = project;
25 | this.isProcessingInner = isProcessingInner;
26 | this.packageName = packageName.replaceAll("\\.", "/");
27 | }
28 |
29 | private Map> dependencies = new HashMap<>();
30 |
31 | public Map> getDependencies() {
32 | if (isProcessingInner) {
33 | return dependencies;
34 | }
35 | return getFilteredDependencies();
36 | }
37 |
38 | public boolean run() {
39 | System.out.println("Analyzing dependencies...");
40 |
41 | File projectDir = new File(project.getBasePath());
42 | if (projectDir.exists()) {
43 | if (isInstantRunEnabled(project.getBasePath())) {
44 | System.err.println("Enabled Instant Run feature detected. " +
45 | "We cannot decompile it. Please, disable Instant Run and rebuild your app.");
46 | } else {
47 | traverseSmaliCodeDir(projectDir);
48 | return true;
49 | }
50 | } else {
51 | System.err.println(projectDir + " does not exist!");
52 | }
53 | return false;
54 | }
55 |
56 | private void traverseSmaliCodeDir(File dir) {
57 | File[] listOfFiles = dir.listFiles();
58 | for (int i = 0; i < listOfFiles.length; i++) {
59 | File currentFile = listOfFiles[i];
60 | if (isSmaliFile(currentFile)) {
61 | //isSmaliFile(currentFile)
62 | processSmaliFile(currentFile);
63 | } else if (currentFile.isDirectory()) {
64 | traverseSmaliCodeDir(currentFile);
65 | }
66 | }
67 | }
68 |
69 | private void processSmaliFile(File file) {
70 | try (BufferedReader br = new BufferedReader(new FileReader(file))) {
71 |
72 | String fileName = file.getName().substring(0, file.getName().lastIndexOf("."));
73 |
74 | if (isClassAnonymous(fileName)) {
75 | fileName = getAnonymousNearestOuter(fileName);
76 | }
77 |
78 | /*
79 | if (!isClassFilterOk(fileName)) {
80 | return;
81 | }
82 | */
83 |
84 | Set classNames = new HashSet<>();
85 | Set dependencyNames = new HashSet<>();
86 |
87 | for (String line; (line = br.readLine()) != null; ) {
88 | try {
89 | classNames.clear();
90 |
91 | parseAndAddClassNames(classNames, line);
92 |
93 | // filtering
94 | for (String fullClassName : classNames) {
95 | if (fullClassName != null && fullClassName.startsWith(packageName)) {
96 | String simpleClassName = getClassSimpleName(fullClassName);
97 | if (isClassOk(simpleClassName, fileName)) { // && isClassFilterOk(simpleClassName)
98 | dependencyNames.add(simpleClassName);
99 | }
100 | }
101 | }
102 | } catch (Exception e) {
103 | System.err.println("Error '" + e.getMessage() + "' occured.");
104 | }
105 | }
106 |
107 | // inner/nested class always depends on the outer class
108 | if (isClassInner(fileName)) {
109 | dependencyNames.add(getOuterClass(fileName));
110 | }
111 |
112 | if (!dependencyNames.isEmpty()) {
113 | addDependencies(fileName, dependencyNames);
114 | }
115 | } catch (FileNotFoundException e) {
116 | System.err.println("Cannot found " + file.getAbsolutePath());
117 | } catch (IOException e) {
118 | System.err.println("Cannot read " + file.getAbsolutePath());
119 | }
120 | }
121 |
122 | /**
123 | * The last filter. Do not show anonymous classes (their dependencies belongs to outer class),
124 | * generated classes, avoid circular dependencies
125 | *
126 | * @param simpleClassName class name to inspect
127 | * @param fileName full class name
128 | * @return true if class is good with these conditions
129 | */
130 | private boolean isClassOk(String simpleClassName, String fileName) {
131 | return !isClassAnonymous(simpleClassName) && !isClassGenerated(simpleClassName)
132 | && !fileName.equals(simpleClassName);
133 | }
134 |
135 | private void parseAndAddClassNames(Set classNames, String line) {
136 | int index = line.indexOf("L");
137 | while (index != -1) {
138 | int colonIndex = line.indexOf(";", index);
139 | if (colonIndex == -1) {
140 | break;
141 | }
142 |
143 | String className = line.substring(index + 1, colonIndex);
144 | if (className.matches("[\\w\\d/$<>]*")) {
145 | int startGenericIndex = className.indexOf("<");
146 | if (startGenericIndex != -1 && className.charAt(startGenericIndex + 1) == 'L') {
147 | // generic
148 | int startGenericInLineIndex = index + startGenericIndex + 1; // index of "<" in the original string
149 | int endGenericInLineIndex = getEndGenericIndex(line, startGenericInLineIndex);
150 | String generic = line.substring(startGenericInLineIndex + 1, endGenericInLineIndex);
151 | parseAndAddClassNames(classNames, generic);
152 | index = line.indexOf("L", endGenericInLineIndex);
153 | className = className.substring(0, startGenericIndex);
154 | } else {
155 | index = line.indexOf("L", colonIndex);
156 | }
157 | } else {
158 | index = line.indexOf("L", index + 1);
159 | continue;
160 | }
161 |
162 | classNames.add(className);
163 | }
164 | }
165 |
166 | private void addDependencies(String className, Set dependenciesList) {
167 | Set depList = dependencies.get(className);
168 | if (depList == null) {
169 | // add this class and its dependencies
170 | dependencies.put(className, dependenciesList);
171 | } else {
172 | // if this class is already added - update its dependencies
173 | depList.addAll(dependenciesList);
174 | }
175 | }
176 |
177 | private Map> getFilteredDependencies() {
178 | Map> filteredDependencies = new HashMap<>();
179 | for (String key : dependencies.keySet()) {
180 | if (!key.contains("$")) {
181 | Set dependencySet = new HashSet<>();
182 | for (String dependency : dependencies.get(key)) {
183 | if (!dependency.contains("$")) {
184 | dependencySet.add(dependency);
185 | }
186 | }
187 | if (dependencySet.size() > 0) {
188 | filteredDependencies.put(key, dependencySet);
189 | }
190 | }
191 | }
192 | return filteredDependencies;
193 | }
194 |
195 | private boolean isClassGenerated(String className) {
196 | return className != null && className.contains("$$");
197 | }
198 |
199 | private boolean isClassInner(String className) {
200 | return className != null && className.contains("$") && !isClassAnonymous(className) && !isClassGenerated(className);
201 | }
202 |
203 | private String getOuterClass(String className) {
204 | return className.substring(0, className.lastIndexOf("$"));
205 | }
206 |
207 | private boolean isClassAnonymous(String className) {
208 | return className != null && className.contains("$")
209 | && isNumeric(className.substring(className.lastIndexOf("$") + 1, className.length()));
210 | }
211 |
212 | private String getAnonymousNearestOuter(String className) {
213 | String[] classes = className.split("\\$");
214 | for (int i = 0; i < classes.length; i++) {
215 | if (isNumeric(classes[i])) {
216 | String anonHolder = "";
217 | for (int j = 0; j < i; j++) {
218 | anonHolder += classes[j] + (j == i - 1 ? "" : "$");
219 | }
220 | return anonHolder;
221 | }
222 | }
223 | return null;
224 | }
225 |
226 | private int getEndGenericIndex(String line, int startGenericIndex) {
227 | int endIndex = line.indexOf(">", startGenericIndex);
228 | for (int i = endIndex + 2; i < line.length(); i += 2) {
229 | if (line.charAt(i) == '>') {
230 | endIndex = i;
231 | }
232 | }
233 | return endIndex;
234 | }
235 |
236 | private String getClassSimpleName(String fullClassName) {
237 | String simpleClassName = fullClassName.substring(fullClassName.lastIndexOf("/") + 1,
238 | fullClassName.length());
239 | int startGenericIndex = simpleClassName.indexOf("<");
240 | if (startGenericIndex != -1) {
241 | simpleClassName = simpleClassName.substring(0, startGenericIndex);
242 | }
243 | return simpleClassName;
244 | }
245 |
246 | private boolean isInstantRunEnabled(String projectPath) {
247 | File unknownDir = new File(projectPath, "unknown");
248 | if (unknownDir.exists() && unknownDir.isDirectory()) {
249 | for (File file : unknownDir.listFiles()) {
250 | if (file.getName().equals("instant-run.zip")) {
251 | return true;
252 | }
253 | }
254 | }
255 | return false;
256 | }
257 |
258 | private boolean isSmaliFile(File file) {
259 | return file.isFile() && file.getName().endsWith(".smali");
260 | }
261 | }
--------------------------------------------------------------------------------
/src/main/java/utils/Strings.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Necati Caner Gaygisiz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *